/* * Copyright (c) 2010-2016 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.task.quartzimpl; import com.evolveum.midpoint.prism.Containerable; import com.evolveum.midpoint.prism.Item; import com.evolveum.midpoint.prism.PrismContainer; 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.PrismProperty; import com.evolveum.midpoint.prism.PrismPropertyDefinition; import com.evolveum.midpoint.prism.PrismPropertyValue; import com.evolveum.midpoint.prism.PrismReference; import com.evolveum.midpoint.prism.PrismReferenceDefinition; import com.evolveum.midpoint.prism.PrismReferenceValue; import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.delta.ChangeType; import com.evolveum.midpoint.prism.delta.ContainerDelta; import com.evolveum.midpoint.prism.delta.ItemDelta; 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.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.prism.query.builder.QueryBuilder; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.DeltaConvertor; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.statistics.EnvironmentalPerformanceInformation; import com.evolveum.midpoint.schema.statistics.IterativeTaskInformation; import com.evolveum.midpoint.schema.statistics.ProvisioningOperation; import com.evolveum.midpoint.schema.statistics.ActionsExecutedInformation; import com.evolveum.midpoint.schema.statistics.StatisticsUtil; import com.evolveum.midpoint.schema.statistics.SynchronizationInformation; import com.evolveum.midpoint.task.api.LightweightIdentifier; import com.evolveum.midpoint.task.api.LightweightTaskHandler; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.task.api.TaskBinding; import com.evolveum.midpoint.task.api.TaskExecutionStatus; import com.evolveum.midpoint.task.api.TaskHandler; import com.evolveum.midpoint.task.api.TaskPersistenceStatus; import com.evolveum.midpoint.task.api.TaskRecurrence; import com.evolveum.midpoint.task.api.TaskRunResult; import com.evolveum.midpoint.task.api.TaskWaitingReason; import com.evolveum.midpoint.task.quartzimpl.handlers.WaitForSubtasksByPollingTaskHandler; import com.evolveum.midpoint.task.quartzimpl.handlers.WaitForTasksTaskHandler; import com.evolveum.midpoint.util.DebugUtil; 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.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.*; import com.evolveum.prism.xml.ns._public.types_3.ItemDeltaType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.jetbrains.annotations.NotNull; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Future; import static com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType.F_MODEL_OPERATION_CONTEXT; import static com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType.F_WORKFLOW_CONTEXT; /** * Implementation of a Task. * * @see TaskManagerQuartzImpl * * Target state (not quite reached as for now): Functionality present in Task is related to the * data structure describing the task itself, i.e. to the embedded TaskType prism and accompanying data. * Everything related to the management of tasks is put into TaskManagerQuartzImpl and its helper classes. * * @author Radovan Semancik * @author Pavol Mederly * */ public class TaskQuartzImpl implements Task { public static final String DOT_INTERFACE = Task.class.getName() + "."; private TaskBinding DEFAULT_BINDING_TYPE = TaskBinding.TIGHT; private static final int TIGHT_BINDING_INTERVAL_LIMIT = 10; private PrismObject<TaskType> taskPrism; private PrismObject<UserType> requestee; // temporary information private EnvironmentalPerformanceInformation environmentalPerformanceInformation = new EnvironmentalPerformanceInformation(); private SynchronizationInformation synchronizationInformation; // has to be explicitly enabled private IterativeTaskInformation iterativeTaskInformation; // has to be explicitly enabled private ActionsExecutedInformation actionsExecutedInformation; // has to be explicitly enabled /** * Lightweight asynchronous subtasks. * Each task here is a LAT, i.e. transient and with assigned lightweight handler. * * This must be synchronized, because interrupt() method uses it. */ private Set<TaskQuartzImpl> lightweightAsynchronousSubtasks = Collections.synchronizedSet(new HashSet<TaskQuartzImpl>()); private Task parentForLightweightAsynchronousTask; // EXPERIMENTAL /* * Task result is stored here as well as in task prism. * * This one is the live value of this task's result. All operations working with this task * should work with this value. This value is explicitly updated from the value in prism * when fetching task from repo (or creating anew), see initializeFromRepo(). * * The value in taskPrism is updated when necessary, e.g. when getting taskPrism * (for example, used when persisting task to repo), etc, see the code. * * Note that this means that we SHOULD NOT get operation result from the prism - we should * use task.getResult() instead! */ private OperationResult taskResult; /** * Is the task handler allowed to run, or should it stop as soon as possible? */ private volatile boolean canRun; private TaskManagerQuartzImpl taskManager; private RepositoryService repositoryService; /** * The code that should be run for asynchronous transient tasks. * (As opposed to asynchronous persistent tasks, where the handler is specified * via Handler URI in task prism object.) */ private LightweightTaskHandler lightweightTaskHandler; /** * Future representing executing (or submitted-to-execution) lightweight task handler. */ private Future lightweightHandlerFuture; /** * An indication whether lighweight hander is currently executing or not. * Used for waiting upon its completion (because java.util.concurrent facilities are not able * to show this for cancelled/interrupted tasks). */ private volatile boolean lightweightHandlerExecuting; private static final Trace LOGGER = TraceManager.getTrace(TaskQuartzImpl.class); private static final Trace PERFORMANCE_ADVISOR = TraceManager.getPerformanceAdvisorTrace(); private TaskQuartzImpl(TaskManagerQuartzImpl taskManager) { this.taskManager = taskManager; this.canRun = true; } //region Constructors /** * Note: This constructor assumes that the task is transient. * @param taskManager * @param taskIdentifier * @param operationName if null, default op. name will be used */ TaskQuartzImpl(TaskManagerQuartzImpl taskManager, LightweightIdentifier taskIdentifier, String operationName) { this(taskManager); this.repositoryService = taskManager.getRepositoryService(); this.taskPrism = createPrism(); setTaskIdentifier(taskIdentifier.toString()); setExecutionStatusTransient(TaskExecutionStatus.RUNNABLE); setRecurrenceStatusTransient(TaskRecurrence.SINGLE); setBindingTransient(DEFAULT_BINDING_TYPE); setProgressTransient(0); setObjectTransient(null); createOrUpdateTaskResult(operationName); setDefaults(); } /** * Assumes that the task is persistent * @param operationName if null, default op. name will be used */ TaskQuartzImpl(TaskManagerQuartzImpl taskManager, PrismObject<TaskType> taskPrism, RepositoryService repositoryService, String operationName) { this(taskManager); this.repositoryService = repositoryService; this.taskPrism = taskPrism; createOrUpdateTaskResult(operationName); setDefaults(); } /** * Analogous to the previous constructor. * * @param taskPrism */ private void replaceTaskPrism(PrismObject<TaskType> taskPrism) { this.taskPrism = taskPrism; updateTaskResult(); setDefaults(); } private PrismObject<TaskType> createPrism() { try { return getPrismContext().createObject(TaskType.class); } catch (SchemaException e) { throw new SystemException(e.getMessage(), e); } } private void setDefaults() { if (getBinding() == null) { setBindingTransient(DEFAULT_BINDING_TYPE); } } private void updateTaskResult() { createOrUpdateTaskResult(null); } private void createOrUpdateTaskResult(String operationName) { OperationResultType resultInPrism = taskPrism.asObjectable().getResult(); if (resultInPrism == null) { if (operationName == null) { resultInPrism = new OperationResult(DOT_INTERFACE + "run").createOperationResultType(); } else { resultInPrism = new OperationResult(operationName).createOperationResultType(); } taskPrism.asObjectable().setResult(resultInPrism); } taskResult = OperationResult.createOperationResult(resultInPrism); } //endregion public PrismObject<TaskType> getTaskPrismObject() { if (taskResult != null) { taskPrism.asObjectable().setResult(taskResult.createOperationResultType()); taskPrism.asObjectable().setResultStatus(taskResult.getStatus().createStatusType()); } return taskPrism; } RepositoryService getRepositoryService() { return repositoryService; } void setRepositoryService(RepositoryService repositoryService) { this.repositoryService = repositoryService; } @Override public boolean isAsynchronous() { return getPersistenceStatus() == TaskPersistenceStatus.PERSISTENT || isLightweightAsynchronousTask(); // note: if it has lightweight task handler, it must be transient } private boolean recreateQuartzTrigger = false; // whether to recreate quartz trigger on next savePendingModifications and/or synchronizeWithQuartz public boolean isRecreateQuartzTrigger() { return recreateQuartzTrigger; } public void setRecreateQuartzTrigger(boolean recreateQuartzTrigger) { this.recreateQuartzTrigger = recreateQuartzTrigger; } private Collection<ItemDelta<?,?>> pendingModifications = null; public void addPendingModification(ItemDelta<?,?> delta) { if (pendingModifications == null) { pendingModifications = new ArrayList<>(); } ItemDelta.merge(pendingModifications, delta); } @Override public void addModification(ItemDelta<?,?> delta) throws SchemaException { addPendingModification(delta); delta.applyTo(taskPrism); } @Override public void addModifications(Collection<ItemDelta<?,?>> deltas) throws SchemaException { for (ItemDelta<?,?> delta : deltas) { addPendingModification(delta); delta.applyTo(taskPrism); } } @Override public void addModificationImmediate(ItemDelta<?, ?> delta, OperationResult parentResult) throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException { addPendingModification(delta); delta.applyTo(taskPrism); savePendingModifications(parentResult); } @Override public void savePendingModifications(OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { if (isTransient()) { return; } if (pendingModifications != null) { synchronized (pendingModifications) { // todo perhaps we should put something like this at more places here... if (!pendingModifications.isEmpty()) { try { repositoryService.modifyObject(TaskType.class, getOid(), pendingModifications, parentResult); } finally { // todo reconsider this (it's not ideal but we need at least to reset pendingModifications to stop repeating applying this change) synchronizeWithQuartzIfNeeded(pendingModifications, parentResult); pendingModifications.clear(); } } } } if (isRecreateQuartzTrigger()) { synchronizeWithQuartz(parentResult); } } @Override public Collection<ItemDelta<?,?>> getPendingModifications() { return pendingModifications; } public void synchronizeWithQuartz(OperationResult parentResult) { taskManager.synchronizeTaskWithQuartz(this, parentResult); setRecreateQuartzTrigger(false); } private static Set<QName> quartzRelatedProperties = new HashSet<QName>(); static { quartzRelatedProperties.add(TaskType.F_BINDING); quartzRelatedProperties.add(TaskType.F_RECURRENCE); quartzRelatedProperties.add(TaskType.F_SCHEDULE); quartzRelatedProperties.add(TaskType.F_HANDLER_URI); } private void synchronizeWithQuartzIfNeeded(Collection<ItemDelta<?,?>> deltas, OperationResult parentResult) { if (isRecreateQuartzTrigger()) { synchronizeWithQuartz(parentResult); return; } for (ItemDelta<?,?> delta : deltas) { if (delta.getParentPath().isEmpty() && quartzRelatedProperties.contains(delta.getElementName())) { synchronizeWithQuartz(parentResult); return; } } } private void processModificationNow(ItemDelta<?,?> delta, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { if (delta != null) { Collection<ItemDelta<?,?>> deltas = new ArrayList<ItemDelta<?,?>>(1); deltas.add(delta); repositoryService.modifyObject(TaskType.class, getOid(), deltas, parentResult); synchronizeWithQuartzIfNeeded(deltas, parentResult); } } private void processModificationBatched(ItemDelta<?,?> delta) { if (delta != null) { addPendingModification(delta); } } /* * Getters and setters * =================== */ /* * Progress / expectedTotal */ @Override public long getProgress() { Long value = taskPrism.getPropertyRealValue(TaskType.F_PROGRESS, Long.class); return value != null ? value : 0; } @Override public void setProgress(long value) { processModificationBatched(setProgressAndPrepareDelta(value)); } @Override public void setProgressImmediate(long value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setProgressAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } @Override public void setProgressTransient(long value) { try { taskPrism.setPropertyRealValue(TaskType.F_PROGRESS, value); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } } private PropertyDelta<?> setProgressAndPrepareDelta(long value) { setProgressTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_PROGRESS, value) : null; } @Override public OperationStatsType getStoredOperationStats() { return taskPrism.asObjectable().getOperationStats(); } public void setOperationStatsTransient(OperationStatsType value) { try { taskPrism.setPropertyRealValue(TaskType.F_OPERATION_STATS, value); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } } public void setOperationStats(OperationStatsType value) { processModificationBatched(setOperationStatsAndPrepareDelta(value)); } private PropertyDelta<?> setOperationStatsAndPrepareDelta(OperationStatsType value) { setOperationStatsTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_OPERATION_STATS, value) : null; } @Override public Long getExpectedTotal() { Long value = taskPrism.getPropertyRealValue(TaskType.F_EXPECTED_TOTAL, Long.class); return value != null ? value : 0; } @Override public void setExpectedTotal(Long value) { processModificationBatched(setExpectedTotalAndPrepareDelta(value)); } @Override public void setExpectedTotalImmediate(Long value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setExpectedTotalAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setExpectedTotalTransient(Long value) { try { taskPrism.setPropertyRealValue(TaskType.F_EXPECTED_TOTAL, value); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } } private PropertyDelta<?> setExpectedTotalAndPrepareDelta(Long value) { setExpectedTotalTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_EXPECTED_TOTAL, value) : null; } /* * Result * * setters set also result status type! */ @Override public OperationResult getResult() { return taskResult; } @Override public void setResult(OperationResult result) { processModificationBatched(setResultAndPrepareDelta(result)); setResultStatusType(result != null ? result.getStatus().createStatusType() : null); } @Override public void setResultImmediate(OperationResult result, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setResultAndPrepareDelta(result), parentResult); setResultStatusTypeImmediate(result != null ? result.getStatus().createStatusType() : null, parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void updateStoredTaskResult() throws SchemaException, ObjectNotFoundException { setResultImmediate(getResult(), new OperationResult("dummy")); } public void setResultTransient(OperationResult result) { this.taskResult = result; this.taskPrism.asObjectable().setResult(result.createOperationResultType()); setResultStatusTypeTransient(result != null ? result.getStatus().createStatusType() : null); } private PropertyDelta<?> setResultAndPrepareDelta(OperationResult result) { setResultTransient(result); if (isPersistent()) { PropertyDelta<?> d = PropertyDelta.createReplaceDeltaOrEmptyDelta(taskManager.getTaskObjectDefinition(), TaskType.F_RESULT, result != null ? result.createOperationResultType() : null); LOGGER.trace("setResult delta = " + d.debugDump()); return d; } else { return null; } } /* * Result status * * We read the status from current 'taskResult', not from prism - to be sure to get the most current value. * However, when updating, we update the result in prism object in order for the result to be stored correctly in * the repo (useful for displaying the result in task list). * * So, setting result type to a value that contradicts current taskResult leads to problems. * Anyway, result type should not be set directly, only when updating OperationResult. */ @Override public OperationResultStatusType getResultStatus() { return taskResult == null ? null : taskResult.getStatus().createStatusType(); } public void setResultStatusType(OperationResultStatusType value) { processModificationBatched(setResultStatusTypeAndPrepareDelta(value)); } public void setResultStatusTypeImmediate(OperationResultStatusType value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { processModificationNow(setResultStatusTypeAndPrepareDelta(value), parentResult); } public void setResultStatusTypeTransient(OperationResultStatusType value) { taskPrism.asObjectable().setResultStatus(value); } private PropertyDelta<?> setResultStatusTypeAndPrepareDelta(OperationResultStatusType value) { setResultStatusTypeTransient(value); if (isPersistent()) { PropertyDelta<?> d = PropertyDelta.createReplaceDeltaOrEmptyDelta(taskManager.getTaskObjectDefinition(), TaskType.F_RESULT_STATUS, value); return d; } else { return null; } } /* * Handler URI */ @Override public String getHandlerUri() { return taskPrism.getPropertyRealValue(TaskType.F_HANDLER_URI, String.class); } public void setHandlerUriTransient(String handlerUri) { try { taskPrism.setPropertyRealValue(TaskType.F_HANDLER_URI, handlerUri); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } } @Override public void setHandlerUriImmediate(String value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setHandlerUriAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } @Override public void setHandlerUri(String value) { processModificationBatched(setHandlerUriAndPrepareDelta(value)); } private PropertyDelta<?> setHandlerUriAndPrepareDelta(String value) { setHandlerUriTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_HANDLER_URI, value) : null; } /* * Other handlers URI stack */ @Override public UriStack getOtherHandlersUriStack() { checkHandlerUriConsistency(); return taskPrism.asObjectable().getOtherHandlersUriStack(); } public void setOtherHandlersUriStackTransient(UriStack value) { try { taskPrism.setPropertyRealValue(TaskType.F_OTHER_HANDLERS_URI_STACK, value); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } checkHandlerUriConsistency(); } public void setOtherHandlersUriStackImmediate(UriStack value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setOtherHandlersUriStackAndPrepareDelta(value), parentResult); checkHandlerUriConsistency(); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setOtherHandlersUriStack(UriStack value) { processModificationBatched(setOtherHandlersUriStackAndPrepareDelta(value)); checkHandlerUriConsistency(); } private PropertyDelta<?> setOtherHandlersUriStackAndPrepareDelta(UriStack value) { setOtherHandlersUriStackTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_OTHER_HANDLERS_URI_STACK, value) : null; } private UriStackEntry popFromOtherHandlersUriStack() { checkHandlerUriConsistency(); UriStack stack = taskPrism.getPropertyRealValue(TaskType.F_OTHER_HANDLERS_URI_STACK, UriStack.class); // is this a live value or a copy? (should be live) if (stack == null || stack.getUriStackEntry().isEmpty()) throw new IllegalStateException("Couldn't pop from OtherHandlersUriStack, because it is null or empty"); int last = stack.getUriStackEntry().size() - 1; UriStackEntry retval = stack.getUriStackEntry().get(last); stack.getUriStackEntry().remove(last); // UriStack stack2 = taskPrism.getPropertyRealValue(TaskType.F_OTHER_HANDLERS_URI_STACK, UriStack.class); // LOGGER.info("Stack size after popping: " + stack.getUriStackEntry().size() // + ", freshly got stack size: " + stack2.getUriStackEntry().size()); setOtherHandlersUriStack(stack); return retval; } // @Override // public void pushHandlerUri(String uri) { // pushHandlerUri(uri, null, null); // } // // @Override // public void pushHandlerUri(String uri, ScheduleType schedule) { // pushHandlerUri(uri, schedule, null); // } @Override public void pushHandlerUri(String uri, ScheduleType schedule, TaskBinding binding) { pushHandlerUri(uri, schedule, binding, (Collection<ItemDelta<?,?>>) null); } @Override public void pushHandlerUri(String uri, ScheduleType schedule, TaskBinding binding, ItemDelta<?,?> delta) { Collection<ItemDelta<?,?>> deltas = null; if (delta != null) { deltas = new ArrayList<ItemDelta<?,?>>(); deltas.add(delta); } pushHandlerUri(uri, schedule, binding, deltas); } /** * Makes (uri, schedule, binding) the current task properties, and pushes current (uri, schedule, binding, extensionChange) * onto the stack. * * @param uri New Handler URI * @param schedule New schedule * @param binding New binding */ @Override public void pushHandlerUri(String uri, ScheduleType schedule, TaskBinding binding, Collection<ItemDelta<?,?>> extensionDeltas) { Validate.notNull(uri); if (binding == null) { binding = bindingFromSchedule(schedule); } checkHandlerUriConsistency(); if (this.getHandlerUri() != null) { UriStack stack = taskPrism.getPropertyRealValue(TaskType.F_OTHER_HANDLERS_URI_STACK, UriStack.class); if (stack == null) { stack = new UriStack(); } UriStackEntry use = new UriStackEntry(); use.setHandlerUri(getHandlerUri()); use.setRecurrence(getRecurrenceStatus().toTaskType()); use.setSchedule(getSchedule()); use.setBinding(getBinding().toTaskType()); if (extensionDeltas != null) { storeExtensionDeltas(use.getExtensionDelta(), extensionDeltas); } stack.getUriStackEntry().add(use); setOtherHandlersUriStack(stack); } setHandlerUri(uri); setSchedule(schedule); setRecurrenceStatus(recurrenceFromSchedule(schedule)); setBinding(binding); this.setRecreateQuartzTrigger(true); // will be applied on modifications save } public ItemDelta<?,?> createExtensionDelta(PrismPropertyDefinition definition, Object realValue) { PrismProperty<?> property = (PrismProperty<?>) definition.instantiate(); property.setRealValue(realValue); PropertyDelta propertyDelta = PropertyDelta.createModificationReplaceProperty(new ItemPath(TaskType.F_EXTENSION, property.getElementName()), definition, realValue); // PropertyDelta propertyDelta = new PropertyDelta(new ItemPath(TaskType.F_EXTENSION, property.getElementName()), definition); // propertyDelta.setValuesToReplace(PrismValue.cloneCollection(property.getValues())); return propertyDelta; } private void storeExtensionDeltas(List<ItemDeltaType> result, Collection<ItemDelta<?,?>> extensionDeltas) { for (ItemDelta itemDelta : extensionDeltas) { Collection<ItemDeltaType> deltaTypes = null; try { deltaTypes = DeltaConvertor.toItemDeltaTypes(itemDelta); } catch (SchemaException e) { throw new SystemException("Unexpected SchemaException when converting extension ItemDelta to ItemDeltaType", e); } result.addAll(deltaTypes); } } // derives default binding form schedule private TaskBinding bindingFromSchedule(ScheduleType schedule) { if (schedule == null) { return DEFAULT_BINDING_TYPE; } else if (schedule.getInterval() != null && schedule.getInterval() != 0) { return schedule.getInterval() <= TIGHT_BINDING_INTERVAL_LIMIT ? TaskBinding.TIGHT : TaskBinding.LOOSE; } else if (StringUtils.isNotEmpty(schedule.getCronLikePattern())) { return TaskBinding.LOOSE; } else { return DEFAULT_BINDING_TYPE; } } private TaskRecurrence recurrenceFromSchedule(ScheduleType schedule) { if (schedule == null) { return TaskRecurrence.SINGLE; } else if (schedule.getInterval() != null && schedule.getInterval() != 0) { return TaskRecurrence.RECURRING; } else if (StringUtils.isNotEmpty(schedule.getCronLikePattern())) { return TaskRecurrence.RECURRING; } else { return TaskRecurrence.SINGLE; } } // @Override // public void replaceCurrentHandlerUri(String newUri, ScheduleType schedule) { // // checkHandlerUriConsistency(); // setHandlerUri(newUri); // setSchedule(schedule); // } @Override public void finishHandler(OperationResult parentResult) throws ObjectNotFoundException, SchemaException { // let us drop the current handler URI and nominate the top of the other // handlers stack as the current one LOGGER.trace("finishHandler called for handler URI {}, task {}", this.getHandlerUri(), this); UriStack otherHandlersUriStack = getOtherHandlersUriStack(); if (otherHandlersUriStack != null && !otherHandlersUriStack.getUriStackEntry().isEmpty()) { UriStackEntry use = popFromOtherHandlersUriStack(); setHandlerUri(use.getHandlerUri()); setRecurrenceStatus(use.getRecurrence() != null ? TaskRecurrence.fromTaskType(use.getRecurrence()) : recurrenceFromSchedule(use.getSchedule())); setSchedule(use.getSchedule()); if (use.getBinding() != null) { setBinding(TaskBinding.fromTaskType(use.getBinding())); } else { setBinding(bindingFromSchedule(use.getSchedule())); } for (ItemDeltaType itemDeltaType : use.getExtensionDelta()) { ItemDelta itemDelta = DeltaConvertor.createItemDelta(itemDeltaType, TaskType.class, taskManager.getPrismContext()); LOGGER.trace("Applying ItemDelta to task extension; task = {}; itemDelta = {}", this, itemDelta.debugDump()); this.modifyExtension(itemDelta); } this.setRecreateQuartzTrigger(true); } else { //setHandlerUri(null); // we want the last handler to remain set so the task can be revived taskManager.closeTaskWithoutSavingState(this, parentResult); // as there are no more handlers, let us close this task } try { savePendingModifications(parentResult); checkDependentTasksOnClose(parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } LOGGER.trace("finishHandler: new current handler uri = {}, new number of handlers = {}", getHandlerUri(), getHandlersCount()); } void checkDependentTasksOnClose(OperationResult result) throws SchemaException, ObjectNotFoundException { if (getExecutionStatus() != TaskExecutionStatus.CLOSED) { return; } for (Task dependent : listDependents(result)) { ((TaskQuartzImpl) dependent).checkDependencies(result); } Task parentTask = getParentTask(result); if (parentTask != null) { ((TaskQuartzImpl) parentTask).checkDependencies(result); } } public void checkDependencies(OperationResult result) throws SchemaException, ObjectNotFoundException { if (getExecutionStatus() != TaskExecutionStatus.WAITING || getWaitingReason() != TaskWaitingReason.OTHER_TASKS) { return; } List<Task> dependencies = listSubtasks(result); dependencies.addAll(listPrerequisiteTasks(result)); LOGGER.trace("Checking {} dependencies for waiting task {}", dependencies.size(), this); for (Task dependency : dependencies) { if (!dependency.isClosed()) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Dependency {} of {} is not closed (status = {})", new Object[] { dependency, this, dependency.getExecutionStatus() }); } return; } } // this could be a bit tricky, taking MID-1683 into account: // when a task finishes its execution, we now leave the last handler set // however, this applies to executable tasks; for WAITING tasks we can safely expect that if there is a handler // on the stack, we can run it (by unpausing the task) if (getHandlerUri() != null) { LOGGER.trace("All dependencies of {} are closed, unpausing the task (it has a handler defined)", this); taskManager.unpauseTask(this, result); } else { LOGGER.trace("All dependencies of {} are closed, closing the task (it has no handler defined).", this); taskManager.closeTask(this, result); } } public int getHandlersCount() { checkHandlerUriConsistency(); int main = getHandlerUri() != null ? 1 : 0; int others = getOtherHandlersUriStack() != null ? getOtherHandlersUriStack().getUriStackEntry().size() : 0; return main + others; } private boolean isOtherHandlersUriStackEmpty() { UriStack stack = taskPrism.asObjectable().getOtherHandlersUriStack(); return stack == null || stack.getUriStackEntry().isEmpty(); } private void checkHandlerUriConsistency() { if (getHandlerUri() == null && !isOtherHandlersUriStackEmpty()) throw new IllegalStateException("Handler URI is null but there is at least one 'other' handler (otherHandlerUriStack size = " + getOtherHandlersUriStack().getUriStackEntry().size() + ")"); } /* * Persistence status */ @Override public TaskPersistenceStatus getPersistenceStatus() { return StringUtils.isEmpty(getOid()) ? TaskPersistenceStatus.TRANSIENT : TaskPersistenceStatus.PERSISTENT; } // public void setPersistenceStatusTransient(TaskPersistenceStatus persistenceStatus) { // this.persistenceStatus = persistenceStatus; // } public boolean isPersistent() { return getPersistenceStatus() == TaskPersistenceStatus.PERSISTENT; } @Override public boolean isTransient() { return getPersistenceStatus() == TaskPersistenceStatus.TRANSIENT; } /* * Oid */ @Override public String getOid() { return taskPrism.getOid(); } public void setOid(String oid) { taskPrism.setOid(oid); } // obviously, there are no "persistent" versions of setOid /* * Task identifier (again, without "persistent" versions) */ @Override public String getTaskIdentifier() { return taskPrism.getPropertyRealValue(TaskType.F_TASK_IDENTIFIER, String.class); } private void setTaskIdentifier(String value) { try { taskPrism.setPropertyRealValue(TaskType.F_TASK_IDENTIFIER, value); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } } /* * Execution status * * IMPORTANT: do not set this attribute explicitly (due to the need of synchronization with Quartz scheduler). * Use task life-cycle methods, like close(), suspendTask(), resumeTask(), and so on. */ @Override public TaskExecutionStatus getExecutionStatus() { TaskExecutionStatusType xmlValue = taskPrism.getPropertyRealValue(TaskType.F_EXECUTION_STATUS, TaskExecutionStatusType.class); if (xmlValue == null) { return null; } return TaskExecutionStatus.fromTaskType(xmlValue); } public void setExecutionStatusTransient(TaskExecutionStatus executionStatus) { try { taskPrism.setPropertyRealValue(TaskType.F_EXECUTION_STATUS, executionStatus.toTaskType()); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } } @Override public void setInitialExecutionStatus(TaskExecutionStatus value) { if (isPersistent()) { throw new IllegalStateException("Initial execution state can be set only on transient tasks."); } taskPrism.asObjectable().setExecutionStatus(value.toTaskType()); } public void setExecutionStatusImmediate(TaskExecutionStatus value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setExecutionStatusAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setExecutionStatus(TaskExecutionStatus value) { processModificationBatched(setExecutionStatusAndPrepareDelta(value)); } private PropertyDelta<?> setExecutionStatusAndPrepareDelta(TaskExecutionStatus value) { setExecutionStatusTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_EXECUTION_STATUS, value.toTaskType()) : null; } @Override public void makeRunnable() { if (!isTransient()) { throw new IllegalStateException("makeRunnable can be invoked only on transient tasks; task = " + this); } setExecutionStatus(TaskExecutionStatus.RUNNABLE); } @Override public void makeWaiting() { if (!isTransient()) { throw new IllegalStateException("makeWaiting can be invoked only on transient tasks; task = " + this); } setExecutionStatus(TaskExecutionStatus.WAITING); } @Override public void makeWaiting(TaskWaitingReason reason) { makeWaiting(); setWaitingReason(reason); } public boolean isClosed() { return getExecutionStatus() == TaskExecutionStatus.CLOSED; } /* * Waiting reason */ @Override public TaskWaitingReason getWaitingReason() { TaskWaitingReasonType xmlValue = taskPrism.asObjectable().getWaitingReason(); if (xmlValue == null) { return null; } return TaskWaitingReason.fromTaskType(xmlValue); } public void setWaitingReasonTransient(TaskWaitingReason value) { try { taskPrism.setPropertyRealValue(TaskType.F_WAITING_REASON, value.toTaskType()); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } } public void setWaitingReason(TaskWaitingReason value) { processModificationBatched(setWaitingReasonAndPrepareDelta(value)); } public void setWaitingReasonImmediate(TaskWaitingReason value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setWaitingReasonAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } private PropertyDelta<?> setWaitingReasonAndPrepareDelta(TaskWaitingReason value) { setWaitingReasonTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_WAITING_REASON, value.toTaskType()) : null; } // "safe" method @Override public void startWaitingForTasksImmediate(OperationResult result) throws SchemaException, ObjectNotFoundException { if (getExecutionStatus() != TaskExecutionStatus.WAITING) { throw new IllegalStateException("Task that has to start waiting for tasks should be in WAITING state (it is in " + getExecutionStatus() + " now)"); } setWaitingReasonImmediate(TaskWaitingReason.OTHER_TASKS, result); checkDependencies(result); } /* * Recurrence status */ public TaskRecurrence getRecurrenceStatus() { TaskRecurrenceType xmlValue = taskPrism.getPropertyRealValue(TaskType.F_RECURRENCE, TaskRecurrenceType.class); if (xmlValue == null) { return null; } return TaskRecurrence.fromTaskType(xmlValue); } @Override public boolean isSingle() { return (getRecurrenceStatus() == TaskRecurrence.SINGLE); } @Override public boolean isCycle() { // TODO: binding return (getRecurrenceStatus() == TaskRecurrence.RECURRING); } public void setRecurrenceStatus(TaskRecurrence value) { processModificationBatched(setRecurrenceStatusAndPrepareDelta(value)); } public void setRecurrenceStatusImmediate(TaskRecurrence value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setRecurrenceStatusAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setRecurrenceStatusTransient(TaskRecurrence value) { try { taskPrism.setPropertyRealValue(TaskType.F_RECURRENCE, value.toTaskType()); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } } private PropertyDelta<?> setRecurrenceStatusAndPrepareDelta(TaskRecurrence value) { setRecurrenceStatusTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_RECURRENCE, value.toTaskType()) : null; } @Override public void makeSingle() { setRecurrenceStatus(TaskRecurrence.SINGLE); setSchedule(new ScheduleType()); } @Override public void makeSingle(ScheduleType schedule) { setRecurrenceStatus(TaskRecurrence.SINGLE); setSchedule(schedule); } @Override public void makeRecurring(ScheduleType schedule) { setRecurrenceStatus(TaskRecurrence.RECURRING); setSchedule(schedule); } @Override public void makeRecurringSimple(int interval) { setRecurrenceStatus(TaskRecurrence.RECURRING); ScheduleType schedule = new ScheduleType(); schedule.setInterval(interval); setSchedule(schedule); } @Override public void makeRecurringCron(String cronLikeSpecification) { setRecurrenceStatus(TaskRecurrence.RECURRING); ScheduleType schedule = new ScheduleType(); schedule.setCronLikePattern(cronLikeSpecification); setSchedule(schedule); } @Override public TaskExecutionConstraintsType getExecutionConstraints() { return taskPrism.asObjectable().getExecutionConstraints(); } @Override public String getGroup() { TaskExecutionConstraintsType executionConstraints = getExecutionConstraints(); return executionConstraints != null ? executionConstraints.getGroup() : null; } /* * Schedule */ @Override public ScheduleType getSchedule() { return taskPrism.asObjectable().getSchedule(); } public void setSchedule(ScheduleType value) { processModificationBatched(setScheduleAndPrepareDelta(value)); } public void setScheduleImmediate(ScheduleType value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setScheduleAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } private void setScheduleTransient(ScheduleType schedule) { taskPrism.asObjectable().setSchedule(schedule); } private PropertyDelta<?> setScheduleAndPrepareDelta(ScheduleType value) { setScheduleTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_SCHEDULE, value) : null; } /* * ThreadStopAction */ @Override public ThreadStopActionType getThreadStopAction() { return taskPrism.asObjectable().getThreadStopAction(); } @Override public void setThreadStopAction(ThreadStopActionType value) { processModificationBatched(setThreadStopActionAndPrepareDelta(value)); } public void setThreadStopActionImmediate(ThreadStopActionType value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setThreadStopActionAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } private void setThreadStopActionTransient(ThreadStopActionType value) { taskPrism.asObjectable().setThreadStopAction(value); } private PropertyDelta<?> setThreadStopActionAndPrepareDelta(ThreadStopActionType value) { setThreadStopActionTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_THREAD_STOP_ACTION, value) : null; } @Override public boolean isResilient() { ThreadStopActionType tsa = getThreadStopAction(); return tsa == null || tsa == ThreadStopActionType.RESCHEDULE || tsa == ThreadStopActionType.RESTART; } /* * Binding */ @Override public TaskBinding getBinding() { TaskBindingType xmlValue = taskPrism.getPropertyRealValue(TaskType.F_BINDING, TaskBindingType.class); if (xmlValue == null) { return null; } return TaskBinding.fromTaskType(xmlValue); } @Override public boolean isTightlyBound() { return getBinding() == TaskBinding.TIGHT; } @Override public boolean isLooselyBound() { return getBinding() == TaskBinding.LOOSE; } @Override public void setBinding(TaskBinding value) { processModificationBatched(setBindingAndPrepareDelta(value)); } @Override public void setBindingImmediate(TaskBinding value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setBindingAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setBindingTransient(TaskBinding value) { try { taskPrism.setPropertyRealValue(TaskType.F_BINDING, value.toTaskType()); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } } private PropertyDelta<?> setBindingAndPrepareDelta(TaskBinding value) { setBindingTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_BINDING, value.toTaskType()) : null; } /* * Owner */ @Override public PrismObject<UserType> getOwner() { PrismReference ownerRef = taskPrism.findReference(TaskType.F_OWNER_REF); if (ownerRef == null) { return null; } return ownerRef.getValue().getObject(); } @Override public void setOwner(PrismObject<UserType> owner) { if (isPersistent()) { throw new IllegalStateException("setOwner method can be called only on transient tasks!"); } PrismReference ownerRef; try { ownerRef = taskPrism.findOrCreateReference(TaskType.F_OWNER_REF); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } ownerRef.getValue().setObject(owner); } PrismObject<UserType> resolveOwnerRef(OperationResult result) throws SchemaException { PrismReference ownerRef = taskPrism.findReference(TaskType.F_OWNER_REF); if (ownerRef == null) { throw new SchemaException("Task "+getOid()+" does not have an owner (missing ownerRef)"); } try { PrismObject<UserType> owner = repositoryService.getObject(UserType.class, ownerRef.getOid(), null, result); ownerRef.getValue().setObject(owner); return owner; } catch (ObjectNotFoundException e) { LoggingUtils.logExceptionAsWarning(LOGGER, "The owner of task {} cannot be found (owner OID: {})", e, getOid(), ownerRef.getOid()); return null; } } @Override public String getChannel() { return taskPrism.asObjectable().getChannel(); } @Override public void setChannel(String value) { processModificationBatched(setChannelAndPrepareDelta(value)); } @Override public void setChannelImmediate(String value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setChannelAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setChannelTransient(String name) { taskPrism.asObjectable().setChannel(name); } private PropertyDelta<?> setChannelAndPrepareDelta(String value) { setChannelTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_CHANNEL, value) : null; } // @Override // public String getChannel() { // PrismProperty<String> channelProperty = taskPrism.findProperty(TaskType.F_CHANNEL); // if (channelProperty == null) { // return null; // } // return channelProperty.getRealValue(); // } // @Override // public void setChannel(String channelUri) { // // TODO: Is this OK? // PrismProperty<String> channelProperty; // try { // channelProperty = taskPrism.findOrCreateProperty(TaskType.F_CHANNEL); // } catch (SchemaException e) { // // This should not happen // throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); // } // channelProperty.setRealValue(channelUri); // } /* * Object */ @Override public ObjectReferenceType getObjectRef() { PrismReference objectRef = taskPrism.findReference(TaskType.F_OBJECT_REF); if (objectRef == null) { return null; } ObjectReferenceType objRefType = new ObjectReferenceType(); objRefType.setOid(objectRef.getOid()); objRefType.setType(objectRef.getValue().getTargetType()); return objRefType; } @Override public void setObjectRef(ObjectReferenceType value) { processModificationBatched(setObjectRefAndPrepareDelta(value)); } @Override public void setObjectRef(String oid, QName type) { ObjectReferenceType objectReferenceType = new ObjectReferenceType(); objectReferenceType.setOid(oid); objectReferenceType.setType(type); setObjectRef(objectReferenceType); } @Override public void setObjectRefImmediate(ObjectReferenceType value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { processModificationNow(setObjectRefAndPrepareDelta(value), parentResult); } public void setObjectRefTransient(ObjectReferenceType objectRefType) { PrismReference objectRef; try { objectRef = taskPrism.findOrCreateReference(TaskType.F_OBJECT_REF); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } objectRef.getValue().setOid(objectRefType.getOid()); objectRef.getValue().setTargetType(objectRefType.getType()); } private ReferenceDelta setObjectRefAndPrepareDelta(ObjectReferenceType value) { setObjectRefTransient(value); PrismReferenceValue prismReferenceValue = new PrismReferenceValue(); prismReferenceValue.setOid(value.getOid()); prismReferenceValue.setTargetType(value.getType()); return isPersistent() ? ReferenceDelta.createModificationReplace(TaskType.F_OBJECT_REF, taskManager.getTaskObjectDefinition(), prismReferenceValue) : null; } @Override public String getObjectOid() { PrismReference objectRef = taskPrism.findReference(TaskType.F_OBJECT_REF); if (objectRef == null) { return null; } return objectRef.getValue().getOid(); } @Override public <T extends ObjectType> PrismObject<T> getObject(Class<T> type, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { // Shortcut PrismReference objectRef = taskPrism.findReference(TaskType.F_OBJECT_REF); if (objectRef == null) { return null; } if (objectRef.getValue().getObject() != null) { PrismObject object = objectRef.getValue().getObject(); if (object.canRepresent(type)) { return (PrismObject<T>) object; } else { throw new IllegalArgumentException("Requested object type "+type+", but the type of object in the task is "+object.getClass()); } } OperationResult result = parentResult.createSubresult(DOT_INTERFACE+"getObject"); result.addContext(OperationResult.CONTEXT_OID, getOid()); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, TaskQuartzImpl.class); try { PrismObject<T> object = repositoryService.getObject(type, objectRef.getOid(), null, result); objectRef.getValue().setObject(object); result.recordSuccess(); return object; } catch (ObjectNotFoundException ex) { result.recordFatalError("Object not found", ex); throw ex; } catch (SchemaException ex) { result.recordFatalError("Schema error", ex); throw ex; } } @Override public void setObjectTransient(PrismObject object) { if (object == null) { PrismReference objectRef = taskPrism.findReference(TaskType.F_OBJECT_REF); if (objectRef != null) { taskPrism.getValue().remove(objectRef); } } else { PrismReference objectRef; try { objectRef = taskPrism.findOrCreateReference(TaskType.F_OBJECT_REF); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } objectRef.getValue().setObject(object); } } /* * Name */ @Override public PolyStringType getName() { return taskPrism.asObjectable().getName(); } @Override public void setName(PolyStringType value) { processModificationBatched(setNameAndPrepareDelta(value)); } @Override public void setName(String value) { processModificationBatched(setNameAndPrepareDelta(new PolyStringType(value))); } @Override public void setNameImmediate(PolyStringType value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { processModificationNow(setNameAndPrepareDelta(value), parentResult); } public void setNameTransient(PolyStringType name) { taskPrism.asObjectable().setName(name); } private PropertyDelta<?> setNameAndPrepareDelta(PolyStringType value) { setNameTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_NAME, value.toPolyString()) : null; } /* * Description */ @Override public String getDescription() { return taskPrism.asObjectable().getDescription(); } @Override public void setDescription(String value) { processModificationBatched(setDescriptionAndPrepareDelta(value)); } @Override public void setDescriptionImmediate(String value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setDescriptionAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setDescriptionTransient(String name) { taskPrism.asObjectable().setDescription(name); } private PropertyDelta<?> setDescriptionAndPrepareDelta(String value) { setDescriptionTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_DESCRIPTION, value) : null; } /* * Parent */ @Override public String getParent() { return taskPrism.asObjectable().getParent(); } @Override public Task getParentTask(OperationResult result) throws SchemaException, ObjectNotFoundException { if (getParent() == null) { return null; } else { return taskManager.getTaskByIdentifier(getParent(), result); } } @Override public Task getParentForLightweightAsynchronousTask() { return parentForLightweightAsynchronousTask; } public void setParent(String value) { processModificationBatched(setParentAndPrepareDelta(value)); } public void setParentImmediate(String value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setParentAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setParentTransient(String name) { taskPrism.asObjectable().setParent(name); } private PropertyDelta<?> setParentAndPrepareDelta(String value) { setParentTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_PARENT, value) : null; } /* * Dependents */ @Override public List<String> getDependents() { return taskPrism.asObjectable().getDependent(); } @Override public List<Task> listDependents(OperationResult parentResult) throws SchemaException, ObjectNotFoundException { OperationResult result = parentResult.createSubresult(DOT_INTERFACE + "listDependents"); result.addContext(OperationResult.CONTEXT_OID, getOid()); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, TaskQuartzImpl.class); List<Task> dependents = new ArrayList<Task>(getDependents().size()); for (String dependentId : getDependents()) { try { Task dependent = taskManager.getTaskByIdentifier(dependentId, result); dependents.add(dependent); } catch (ObjectNotFoundException e) { LOGGER.trace("Dependent task {} was not found. Probably it was not yet stored to repo; we just ignore it.", dependentId); } } result.recordSuccessIfUnknown(); return dependents; } @Override public void addDependent(String value) { processModificationBatched(addDependentAndPrepareDelta(value)); } public void addDependentTransient(String name) { taskPrism.asObjectable().getDependent().add(name); } private PropertyDelta<?> addDependentAndPrepareDelta(String value) { addDependentTransient(value); return isPersistent() ? PropertyDelta.createAddDelta( taskManager.getTaskObjectDefinition(), TaskType.F_DEPENDENT, value) : null; } @Override public void deleteDependent(String value) { processModificationBatched(deleteDependentAndPrepareDelta(value)); } public void deleteDependentTransient(String name) { taskPrism.asObjectable().getDependent().remove(name); } private PropertyDelta<?> deleteDependentAndPrepareDelta(String value) { deleteDependentTransient(value); return isPersistent() ? PropertyDelta.createDeleteDelta( taskManager.getTaskObjectDefinition(), TaskType.F_DEPENDENT, value) : null; } /* * Extension */ @Override public PrismContainer<?> getExtension() { return taskPrism.getExtension(); } @Override public <T> PrismProperty<T> getExtensionProperty(QName propertyName) { if (getExtension() != null) { return getExtension().findProperty(propertyName); } else { return null; } } @Override public <T> T getExtensionPropertyRealValue(QName propertyName) { PrismProperty<T> property = getExtensionProperty(propertyName); if (property == null || property.isEmpty()) { return null; } else { return property.getRealValue(); } } @Override public Item<?,?> getExtensionItem(QName propertyName) { if (getExtension() != null) { return getExtension().findItem(propertyName); } else { return null; } } @Override public PrismReference getExtensionReference(QName propertyName) { Item item = getExtensionItem(propertyName); return (PrismReference) item; } @Override public void setExtensionItem(Item item) throws SchemaException { if (item instanceof PrismProperty) { setExtensionProperty((PrismProperty) item); } else if (item instanceof PrismReference) { setExtensionReference((PrismReference) item); } else if (item instanceof PrismContainer) { setExtensionContainer((PrismContainer) item); } else { throw new IllegalArgumentException("Unknown kind of item: " + (item == null ? "(null)" : item.getClass())); } } @Override public void setExtensionProperty(PrismProperty<?> property) throws SchemaException { processModificationBatched(setExtensionPropertyAndPrepareDelta(property.getElementName(), property.getDefinition(), PrismValue.cloneCollection(property.getValues()))); } @Override public void setExtensionReference(PrismReference reference) throws SchemaException { processModificationBatched(setExtensionReferenceAndPrepareDelta(reference.getElementName(), reference.getDefinition(), PrismValue.cloneCollection(reference.getValues()))); } @Override public void addExtensionReference(PrismReference reference) throws SchemaException { processModificationBatched(addExtensionReferenceAndPrepareDelta(reference.getElementName(), reference.getDefinition(), PrismValue.cloneCollection(reference.getValues()))); } @Override public <C extends Containerable> void setExtensionContainer(PrismContainer<C> container) throws SchemaException { processModificationBatched(setExtensionContainerAndPrepareDelta(container.getElementName(), container.getDefinition(), PrismValue.cloneCollection(container.getValues()))); } // use this method to avoid cloning the value @Override public <T> void setExtensionPropertyValue(QName propertyName, T value) throws SchemaException { PrismPropertyDefinition propertyDef = getPrismContext().getSchemaRegistry().findPropertyDefinitionByElementName(propertyName); if (propertyDef == null) { throw new SchemaException("Unknown property " + propertyName); } ArrayList<PrismPropertyValue<T>> values = new ArrayList(1); if (value != null) { values.add(new PrismPropertyValue<T>(value)); } processModificationBatched(setExtensionPropertyAndPrepareDelta(propertyName, propertyDef, values)); } @Override public <T> void setExtensionPropertyValueTransient(QName propertyName, T value) throws SchemaException { PrismPropertyDefinition propertyDef = getPrismContext().getSchemaRegistry().findPropertyDefinitionByElementName(propertyName); if (propertyDef == null) { throw new SchemaException("Unknown property " + propertyName); } ArrayList<PrismPropertyValue<T>> values = new ArrayList(1); if (value != null) { values.add(new PrismPropertyValue<T>(value)); } ItemDelta delta = new PropertyDelta(new ItemPath(TaskType.F_EXTENSION, propertyName), propertyDef, getPrismContext()); delta.setValuesToReplace(values); Collection<ItemDelta<?,?>> modifications = new ArrayList<>(1); modifications.add(delta); PropertyDelta.applyTo(modifications, taskPrism); } // use this method to avoid cloning the value @Override public <T extends Containerable> void setExtensionContainerValue(QName containerName, T value) throws SchemaException { PrismContainerDefinition containerDef = getPrismContext().getSchemaRegistry().findContainerDefinitionByElementName(containerName); if (containerDef == null) { throw new SchemaException("Unknown container item " + containerName); } ArrayList<PrismContainerValue<T>> values = new ArrayList(1); values.add(value.asPrismContainerValue()); processModificationBatched(setExtensionContainerAndPrepareDelta(containerName, containerDef, values)); } @Override public void addExtensionProperty(PrismProperty<?> property) throws SchemaException { processModificationBatched(addExtensionPropertyAndPrepareDelta(property.getElementName(), property.getDefinition(), PrismValue.cloneCollection(property.getValues()))); } @Override public void deleteExtensionProperty(PrismProperty<?> property) throws SchemaException { processModificationBatched(deleteExtensionPropertyAndPrepareDelta(property.getElementName(), property.getDefinition(), PrismValue.cloneCollection(property.getValues()))); } @Override public void modifyExtension(ItemDelta itemDelta) throws SchemaException { if (itemDelta.getPath() == null || itemDelta.getPath().first() == null || !TaskType.F_EXTENSION.equals(ItemPath.getName(itemDelta.getPath().first()))) { throw new IllegalArgumentException("modifyExtension must modify the Task extension element; however, the path is " + itemDelta.getPath()); } processModificationBatched(modifyExtensionAndPrepareDelta(itemDelta)); } @Override public void setExtensionPropertyImmediate(PrismProperty<?> property, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setExtensionPropertyAndPrepareDelta(property.getElementName(), property.getDefinition(), PrismValue.cloneCollection(property.getValues())), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } private ItemDelta<?,?> setExtensionPropertyAndPrepareDelta(QName itemName, PrismPropertyDefinition definition, Collection<? extends PrismPropertyValue> values) throws SchemaException { ItemDelta delta = new PropertyDelta(new ItemPath(TaskType.F_EXTENSION, itemName), definition, getPrismContext()); return setExtensionItemAndPrepareDeltaCommon(delta, values); } private ItemDelta<?,?> setExtensionReferenceAndPrepareDelta(QName itemName, PrismReferenceDefinition definition, Collection<? extends PrismReferenceValue> values) throws SchemaException { ItemDelta delta = new ReferenceDelta(new ItemPath(TaskType.F_EXTENSION, itemName), definition, getPrismContext()); return setExtensionItemAndPrepareDeltaCommon(delta, values); } private ItemDelta<?,?> addExtensionReferenceAndPrepareDelta(QName itemName, PrismReferenceDefinition definition, Collection<? extends PrismReferenceValue> values) throws SchemaException { ItemDelta delta = new ReferenceDelta(new ItemPath(TaskType.F_EXTENSION, itemName), definition, getPrismContext()); return addExtensionItemAndPrepareDeltaCommon(delta, values); } private ItemDelta<?,?> setExtensionContainerAndPrepareDelta(QName itemName, PrismContainerDefinition definition, Collection<? extends PrismContainerValue> values) throws SchemaException { ItemDelta delta = new ContainerDelta(new ItemPath(TaskType.F_EXTENSION, itemName), definition, getPrismContext()); return setExtensionItemAndPrepareDeltaCommon(delta, values); } private <V extends PrismValue> ItemDelta<?,?> setExtensionItemAndPrepareDeltaCommon(ItemDelta delta, Collection<V> values) throws SchemaException { // these values should have no parent, otherwise the following will fail delta.setValuesToReplace(values); Collection<ItemDelta<?,?>> modifications = new ArrayList<>(1); modifications.add(delta); PropertyDelta.applyTo(modifications, taskPrism); // i.e. here we apply changes only locally (in memory) return isPersistent() ? delta : null; } private <V extends PrismValue> ItemDelta<?,?> addExtensionItemAndPrepareDeltaCommon(ItemDelta delta, Collection<V> values) throws SchemaException { // these values should have no parent, otherwise the following will fail delta.addValuesToAdd(values); Collection<ItemDelta<?,?>> modifications = new ArrayList<>(1); modifications.add(delta); PropertyDelta.applyTo(modifications, taskPrism); // i.e. here we apply changes only locally (in memory) return isPersistent() ? delta : null; } private ItemDelta<?,?> modifyExtensionAndPrepareDelta(ItemDelta<?,?> delta) throws SchemaException { Collection<ItemDelta<?,?>> modifications = new ArrayList<ItemDelta<?,?>>(1); modifications.add(delta); PropertyDelta.applyTo(modifications, taskPrism); // i.e. here we apply changes only locally (in memory) return isPersistent() ? delta : null; } private ItemDelta<?,?> addExtensionPropertyAndPrepareDelta(QName itemName, PrismPropertyDefinition definition, Collection<? extends PrismPropertyValue> values) throws SchemaException { ItemDelta delta = new PropertyDelta(new ItemPath(TaskType.F_EXTENSION, itemName), definition, getPrismContext()); delta.addValuesToAdd(values); Collection<ItemDelta<?,?>> modifications = new ArrayList<>(1); modifications.add(delta); PropertyDelta.applyTo(modifications, taskPrism); // i.e. here we apply changes only locally (in memory) return isPersistent() ? delta : null; } private ItemDelta<?,?> deleteExtensionPropertyAndPrepareDelta(QName itemName, PrismPropertyDefinition definition, Collection<? extends PrismPropertyValue> values) throws SchemaException { ItemDelta delta = new PropertyDelta(new ItemPath(TaskType.F_EXTENSION, itemName), definition, getPrismContext()); delta.addValuesToDelete(values); Collection<ItemDelta<?,?>> modifications = new ArrayList<>(1); modifications.add(delta); PropertyDelta.applyTo(modifications, taskPrism); // i.e. here we apply changes only locally (in memory) return isPersistent() ? delta : null; } /* * Requestee */ @Override public PrismObject<UserType> getRequestee() { return requestee; } @Override public void setRequesteeTransient(PrismObject<UserType> user) { requestee = user; } /* * Model operation context */ @Override public LensContextType getModelOperationContext() { return taskPrism.asObjectable().getModelOperationContext(); } @Override public void setModelOperationContext(LensContextType value) throws SchemaException { processModificationBatched(setModelOperationContextAndPrepareDelta(value)); } //@Override public void setModelOperationContextImmediate(LensContextType value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setModelOperationContextAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setModelOperationContextTransient(LensContextType value) { taskPrism.asObjectable().setModelOperationContext(value); } private ItemDelta<?, ?> setModelOperationContextAndPrepareDelta(LensContextType value) throws SchemaException { setModelOperationContextTransient(value); if (!isPersistent()) { return null; } if (value != null) { return DeltaBuilder.deltaFor(TaskType.class, getPrismContext()) .item(F_MODEL_OPERATION_CONTEXT).replace(value.asPrismContainerValue().clone()) .asItemDelta(); } else { return DeltaBuilder.deltaFor(TaskType.class, getPrismContext()) .item(F_MODEL_OPERATION_CONTEXT).replace() .asItemDelta(); } } /* * Workflow context */ public void setWorkflowContext(WfContextType value) throws SchemaException { processModificationBatched(setWorkflowContextAndPrepareDelta(value)); } //@Override public void setWorkflowContextImmediate(WfContextType value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setWorkflowContextAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setWorkflowContextTransient(WfContextType value) { taskPrism.asObjectable().setWorkflowContext(value); } private ItemDelta<?, ?> setWorkflowContextAndPrepareDelta(WfContextType value) throws SchemaException { setWorkflowContextTransient(value); if (!isPersistent()) { return null; } if (value != null) { return DeltaBuilder.deltaFor(TaskType.class, getPrismContext()) .item(F_WORKFLOW_CONTEXT).replace(value.asPrismContainerValue().clone()) .asItemDelta(); } else { return DeltaBuilder.deltaFor(TaskType.class, getPrismContext()) .item(F_WORKFLOW_CONTEXT).replace() .asItemDelta(); } } @Override public WfContextType getWorkflowContext() { return taskPrism.asObjectable().getWorkflowContext(); } @Override public void initializeWorkflowContextImmediate(String processInstanceId, OperationResult result) throws SchemaException, ObjectNotFoundException { WfContextType wfContextType = new WfContextType(getPrismContext()); wfContextType.setProcessInstanceId(processInstanceId); setWorkflowContextImmediate(wfContextType, result); } // @Override // public PrismReference getRequesteeRef() { // return (PrismReference) getExtensionItem(SchemaConstants.C_TASK_REQUESTEE_REF); // } // @Override // public String getRequesteeOid() { // PrismProperty<String> property = (PrismProperty<String>) getExtensionProperty(SchemaConstants.C_TASK_REQUESTEE_OID); // if (property != null) { // return property.getRealValue(); // } else { // return null; // } // } // @Override // public void setRequesteeRef(PrismReferenceValue requesteeRef) throws SchemaException { // PrismReferenceDefinition itemDefinition = new PrismReferenceDefinition(SchemaConstants.C_TASK_REQUESTEE_REF, // SchemaConstants.C_TASK_REQUESTEE_REF, ObjectReferenceType.COMPLEX_TYPE, taskManager.getPrismContext()); // itemDefinition.setTargetTypeName(UserType.COMPLEX_TYPE); // // PrismReference ref = new PrismReference(SchemaConstants.C_TASK_REQUESTEE_REF); // ref.setDefinition(itemDefinition); // ref.add(requesteeRef); // setExtensionReference(ref); // } // @Override // public void setRequesteeRef(PrismObject<UserType> requestee) throws SchemaException { // Validate.notNull(requestee.getOid()); // PrismReferenceValue value = new PrismReferenceValue(requestee.getOid()); // value.setTargetType(UserType.COMPLEX_TYPE); // setRequesteeRef(value); // } // @Override // public void setRequesteeOid(String oid) throws SchemaException { // setExtensionProperty(prepareRequesteeProperty(oid)); // } // @Override // public void setRequesteeOidImmediate(String oid, OperationResult result) throws SchemaException, ObjectNotFoundException { // setExtensionPropertyImmediate(prepareRequesteeProperty(oid), result); // } // private PrismProperty<String> prepareRequesteeProperty(String oid) { // PrismPropertyDefinition definition = taskManager.getPrismContext().getSchemaRegistry().findPropertyDefinitionByElementName(SchemaConstants.C_TASK_REQUESTEE_OID); // if (definition == null) { // throw new SystemException("No definition for " + SchemaConstants.C_TASK_REQUESTEE_OID); // } // PrismProperty<String> property = definition.instantiate(); // property.setRealValue(oid); // return property; // } /* * Node */ @Override public String getNode() { return taskPrism.asObjectable().getNode(); } public void setNode(String value) { processModificationBatched(setNodeAndPrepareDelta(value)); } public void setNodeImmediate(String value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setNodeAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setNodeTransient(String value) { taskPrism.asObjectable().setNode(value); } private PropertyDelta<?> setNodeAndPrepareDelta(String value) { setNodeTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_NODE, value) : null; } /* * Last run start timestamp */ @Override public Long getLastRunStartTimestamp() { XMLGregorianCalendar gc = taskPrism.asObjectable().getLastRunStartTimestamp(); return gc != null ? new Long(XmlTypeConverter.toMillis(gc)) : null; } public void setLastRunStartTimestamp(Long value) { processModificationBatched(setLastRunStartTimestampAndPrepareDelta(value)); } public void setLastRunStartTimestampImmediate(Long value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setLastRunStartTimestampAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setLastRunStartTimestampTransient(Long value) { taskPrism.asObjectable().setLastRunStartTimestamp( XmlTypeConverter.createXMLGregorianCalendar(value)); } private PropertyDelta<?> setLastRunStartTimestampAndPrepareDelta(Long value) { setLastRunStartTimestampTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_LAST_RUN_START_TIMESTAMP, taskPrism.asObjectable().getLastRunStartTimestamp()) : null; } /* * Last run finish timestamp */ @Override public Long getLastRunFinishTimestamp() { XMLGregorianCalendar gc = taskPrism.asObjectable().getLastRunFinishTimestamp(); return gc != null ? new Long(XmlTypeConverter.toMillis(gc)) : null; } public void setLastRunFinishTimestamp(Long value) { processModificationBatched(setLastRunFinishTimestampAndPrepareDelta(value)); } public void setLastRunFinishTimestampImmediate(Long value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setLastRunFinishTimestampAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setLastRunFinishTimestampTransient(Long value) { taskPrism.asObjectable().setLastRunFinishTimestamp( XmlTypeConverter.createXMLGregorianCalendar(value)); } private PropertyDelta<?> setLastRunFinishTimestampAndPrepareDelta(Long value) { setLastRunFinishTimestampTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_LAST_RUN_FINISH_TIMESTAMP, taskPrism.asObjectable().getLastRunFinishTimestamp()) : null; } /* * Completion timestamp */ @Override public Long getCompletionTimestamp() { XMLGregorianCalendar gc = taskPrism.asObjectable().getCompletionTimestamp(); return gc != null ? new Long(XmlTypeConverter.toMillis(gc)) : null; } public void setCompletionTimestamp(Long value) { processModificationBatched(setCompletionTimestampAndPrepareDelta(value)); } public void setCompletionTimestampImmediate(Long value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setCompletionTimestampAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setCompletionTimestampTransient(Long value) { taskPrism.asObjectable().setCompletionTimestamp( XmlTypeConverter.createXMLGregorianCalendar(value)); } private PropertyDelta<?> setCompletionTimestampAndPrepareDelta(Long value) { setCompletionTimestampTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_COMPLETION_TIMESTAMP, taskPrism.asObjectable().getCompletionTimestamp()) : null; } /* * Next run start time */ @Override public Long getNextRunStartTime(OperationResult parentResult) { return taskManager.getNextRunStartTime(getOid(), parentResult); } @Override public String debugDump() { return debugDump(0); } @Override public String debugDump(int indent) { StringBuilder sb = new StringBuilder(); DebugUtil.indentDebugDump(sb, indent); sb.append("Task("); sb.append(TaskQuartzImpl.class.getName()); sb.append(")\n"); sb.append(taskPrism.debugDump(indent + 1)); sb.append("\n persistenceStatus: "); sb.append(getPersistenceStatus()); sb.append("\n result: "); if (taskResult ==null) { sb.append("null"); } else { sb.append(taskResult.debugDump()); } return sb.toString(); } /* * Handler and category */ public TaskHandler getHandler() { String handlerUri = taskPrism.asObjectable().getHandlerUri(); return handlerUri != null ? taskManager.getHandler(handlerUri) : null; } @Override public void setCategory(String value) { processModificationBatched(setCategoryAndPrepareDelta(value)); } public void setCategoryImmediate(String value, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { try { processModificationNow(setCategoryAndPrepareDelta(value), parentResult); } catch (ObjectAlreadyExistsException ex) { throw new SystemException(ex); } } public void setCategoryTransient(String value) { try { taskPrism.setPropertyRealValue(TaskType.F_CATEGORY, value); } catch (SchemaException e) { // This should not happen throw new IllegalStateException("Internal schema error: "+e.getMessage(),e); } } private PropertyDelta<?> setCategoryAndPrepareDelta(String value) { setCategoryTransient(value); return isPersistent() ? PropertyDelta.createReplaceDeltaOrEmptyDelta( taskManager.getTaskObjectDefinition(), TaskType.F_CATEGORY, value) : null; } @Override public String getCategory() { return taskPrism.asObjectable().getCategory(); } public String getCategoryFromHandler() { TaskHandler h = getHandler(); if (h != null) { return h.getCategoryName(this); } else { return null; } } /* * Other methods */ @Override public void refresh(OperationResult parentResult) throws ObjectNotFoundException, SchemaException { OperationResult result = parentResult.createSubresult(DOT_INTERFACE+"refresh"); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, TaskQuartzImpl.class); result.addContext(OperationResult.CONTEXT_OID, getOid()); if (!isPersistent()) { // Nothing to do for transient tasks result.recordSuccess(); return; } PrismObject<TaskType> repoObj; try { repoObj = repositoryService.getObject(TaskType.class, getOid(), null, result); } catch (ObjectNotFoundException ex) { result.recordFatalError("Object not found", ex); throw ex; } catch (SchemaException ex) { result.recordFatalError("Schema error", ex); throw ex; } updateTaskInstance(repoObj, result); result.recordSuccess(); } private void updateTaskInstance(PrismObject<TaskType> taskPrism, OperationResult parentResult) throws SchemaException { OperationResult result = parentResult.createSubresult(DOT_INTERFACE + "updateTaskInstance"); result.addArbitraryObjectAsParam("task", this); result.addParam("taskPrism", taskPrism); replaceTaskPrism(taskPrism); resolveOwnerRef(result); result.recordSuccessIfUnknown(); } // public void modify(Collection<? extends ItemDelta> modifications, OperationResult parentResult) throws ObjectNotFoundException, SchemaException { // throw new UnsupportedOperationException("Generic task modification is not supported. Please use concrete setter methods to modify a task"); // PropertyDelta.applyTo(modifications, taskPrism); // if (isPersistent()) { // getRepositoryService().modifyObject(TaskType.class, getOid(), modifications, parentResult); // } // } /** * Signal the task to shut down. * It may not stop immediately, but it should stop eventually. * * BEWARE, this method has to be invoked on the same Task instance that is executing. * If called e.g. on a Task just retrieved from the repository, it will have no effect whatsoever. */ public void unsetCanRun() { // beware: Do not touch task prism here, because this method can be called asynchronously canRun = false; } @Override public boolean canRun() { return canRun; } // checks latest start time (useful for recurring tightly coupled tasks) public boolean stillCanStart() { if (getSchedule() != null && getSchedule().getLatestStartTime() != null) { long lst = getSchedule().getLatestStartTime().toGregorianCalendar().getTimeInMillis(); return lst >= System.currentTimeMillis(); } else { return true; } } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Task(id:" + getTaskIdentifier() + ", name:" + getName() + ", oid:" + getOid() + ")"; } @Override public int hashCode() { return taskPrism.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TaskQuartzImpl other = (TaskQuartzImpl) obj; // if (persistenceStatus != other.persistenceStatus) // return false; if (taskResult == null) { if (other.taskResult != null) return false; } else if (!taskResult.equals(other.taskResult)) return false; if (taskPrism == null) { if (other.taskPrism != null) return false; } else if (!taskPrism.equals(other.taskPrism)) return false; return true; } private PrismContext getPrismContext() { return taskManager.getPrismContext(); } @Override public Task createSubtask() { TaskQuartzImpl sub = (TaskQuartzImpl) taskManager.createTaskInstance(); sub.setParent(this.getTaskIdentifier()); sub.setOwner(this.getOwner()); sub.setChannel(this.getChannel()); // taskManager.registerTransientSubtask(sub, this); LOGGER.trace("New subtask {} has been created.", sub.getTaskIdentifier()); return sub; } @Override public Task createSubtask(LightweightTaskHandler handler) { TaskQuartzImpl sub = ((TaskQuartzImpl) createSubtask()); sub.setLightweightTaskHandler(handler); lightweightAsynchronousSubtasks.add(sub); sub.parentForLightweightAsynchronousTask = this; return sub; } @Deprecated public TaskRunResult waitForSubtasks(Integer interval, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { return waitForSubtasks(interval, null, parentResult); } @Deprecated public TaskRunResult waitForSubtasks(Integer interval, Collection<ItemDelta<?,?>> extensionDeltas, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { OperationResult result = parentResult.createSubresult(DOT_INTERFACE + "waitForSubtasks"); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, TaskQuartzImpl.class); result.addContext(OperationResult.CONTEXT_OID, getOid()); TaskRunResult trr = new TaskRunResult(); trr.setProgress(this.getProgress()); trr.setRunResultStatus(TaskRunResult.TaskRunResultStatus.RESTART_REQUESTED); trr.setOperationResult(null); ScheduleType schedule = new ScheduleType(); if (interval != null) { schedule.setInterval(interval); } else { schedule.setInterval(30); } pushHandlerUri(WaitForSubtasksByPollingTaskHandler.HANDLER_URI, schedule, null, extensionDeltas); setBinding(TaskBinding.LOOSE); savePendingModifications(result); return trr; } // @Override // public boolean waitForTransientSubtasks(long timeout, OperationResult parentResult) { // long endTime = System.currentTimeMillis() + timeout; // for (Task t : transientSubtasks) { // TaskQuartzImpl tqi = (TaskQuartzImpl) t; // if (!tqi.lightweightTaskHandlerFinished()) { // long wait = endTime - System.currentTimeMillis(); // if (wait <= 0) { // return false; // } // try { // tqi.threadForLightweightTaskHandler.join(wait); // } catch (InterruptedException e) { // return false; // } // if (tqi.threadForLightweightTaskHandler.isAlive()) { // return false; // } // } // } // LOGGER.trace("All transient subtasks finished for task {}", this); // return true; // } public List<PrismObject<TaskType>> listSubtasksRaw(OperationResult parentResult) throws SchemaException { OperationResult result = parentResult.createSubresult(DOT_INTERFACE + "listSubtasksRaw"); result.addContext(OperationResult.CONTEXT_OID, getOid()); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, TaskQuartzImpl.class); if (!isPersistent()) { result.recordSuccessIfUnknown(); return new ArrayList<>(0); } return taskManager.listSubtasksForTask(getTaskIdentifier(), result); } public List<PrismObject<TaskType>> listPrerequisiteTasksRaw(OperationResult parentResult) throws SchemaException { OperationResult result = parentResult.createSubresult(DOT_INTERFACE + "listPrerequisiteTasksRaw"); result.addContext(OperationResult.CONTEXT_OID, getOid()); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, TaskQuartzImpl.class); ObjectQuery query = QueryBuilder.queryFor(TaskType.class, getPrismContext()) .item(TaskType.F_DEPENDENT).eq(getTaskIdentifier()) .build(); List<PrismObject<TaskType>> list = taskManager.getRepositoryService().searchObjects(TaskType.class, query, null, result); result.recordSuccessIfUnknown(); return list; } @Override public List<Task> listSubtasks(OperationResult parentResult) throws SchemaException { OperationResult result = parentResult.createSubresult(DOT_INTERFACE + "listSubtasks"); result.addContext(OperationResult.CONTEXT_OID, getOid()); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, TaskQuartzImpl.class); return listSubtasksInternal(result); } private List<Task> listSubtasksInternal(OperationResult result) throws SchemaException { List<Task> retval = new ArrayList<>(); // persistent subtasks retval.addAll(taskManager.resolveTasksFromTaskTypes(listSubtasksRaw(result), result)); // transient asynchronous subtasks - must be taken from the running task instance! retval.addAll(taskManager.getTransientSubtasks(this)); return retval; } @Override public List<Task> listSubtasksDeeply(OperationResult parentResult) throws SchemaException { OperationResult result = parentResult.createSubresult(DOT_INTERFACE + "listSubtasksDeeply"); result.addContext(OperationResult.CONTEXT_OID, getOid()); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, TaskQuartzImpl.class); ArrayList<Task> retval = new ArrayList<Task>(); addSubtasks(retval, this, result); return retval; } private void addSubtasks(ArrayList<Task> tasks, TaskQuartzImpl taskToProcess, OperationResult result) throws SchemaException { for (Task task : taskToProcess.listSubtasksInternal(result)) { tasks.add(task); addSubtasks(tasks, (TaskQuartzImpl) task, result); } } @Override public List<Task> listPrerequisiteTasks(OperationResult parentResult) throws SchemaException { OperationResult result = parentResult.createSubresult(DOT_INTERFACE + "listPrerequisiteTasks"); result.addContext(OperationResult.CONTEXT_OID, getOid()); result.addContext(OperationResult.CONTEXT_IMPLEMENTATION_CLASS, TaskQuartzImpl.class); return taskManager.resolveTasksFromTaskTypes(listPrerequisiteTasksRaw(result), result); } // private List<Task> resolveTasksFromIdentifiers(OperationResult result, List<String> identifiers) throws SchemaException { // List<Task> tasks = new ArrayList<Task>(identifiers.size()); // for (String identifier : identifiers) { // tasks.add(taskManager.getTaskByIdentifier(result)); // } // // result.recordSuccessIfUnknown(); // return tasks; // } @Override public void pushWaitForTasksHandlerUri() { pushHandlerUri(WaitForTasksTaskHandler.HANDLER_URI, new ScheduleType(), null); } public void setLightweightTaskHandler(LightweightTaskHandler lightweightTaskHandler) { this.lightweightTaskHandler = lightweightTaskHandler; } @Override public LightweightTaskHandler getLightweightTaskHandler() { return lightweightTaskHandler; } @Override public boolean isLightweightAsynchronousTask() { return lightweightTaskHandler != null; } void setLightweightHandlerFuture(Future lightweightHandlerFuture) { this.lightweightHandlerFuture = lightweightHandlerFuture; } public Future getLightweightHandlerFuture() { return lightweightHandlerFuture; } @Override public Set<? extends Task> getLightweightAsynchronousSubtasks() { return Collections.unmodifiableSet(lightweightAsynchronousSubtasks); } @Override public Set<? extends Task> getRunningLightweightAsynchronousSubtasks() { // beware: Do not touch task prism here, because this method can be called asynchronously Set<Task> retval = new HashSet<>(); for (Task subtask : getLightweightAsynchronousSubtasks()) { if (subtask.getExecutionStatus() == TaskExecutionStatus.RUNNABLE && subtask.lightweightHandlerStartRequested()) { retval.add(subtask); } } return Collections.unmodifiableSet(retval); } @Override public boolean lightweightHandlerStartRequested() { return lightweightHandlerFuture != null; } // just a shortcut @Override public void startLightweightHandler() { taskManager.startLightweightTask(this); } public void setLightweightHandlerExecuting(boolean lightweightHandlerExecuting) { this.lightweightHandlerExecuting = lightweightHandlerExecuting; } public boolean isLightweightHandlerExecuting() { return lightweightHandlerExecuting; } // Operational data private EnvironmentalPerformanceInformation getEnvironmentalPerformanceInformation() { return environmentalPerformanceInformation; } private SynchronizationInformation getSynchronizationInformation() { return synchronizationInformation; } private IterativeTaskInformation getIterativeTaskInformation() { return iterativeTaskInformation; } public ActionsExecutedInformation getActionsExecutedInformation() { return actionsExecutedInformation; } @Override public OperationStatsType getAggregatedLiveOperationStats() { EnvironmentalPerformanceInformationType env = getAggregateEnvironmentalPerformanceInformation(); IterativeTaskInformationType itit = getAggregateIterativeTaskInformation(); SynchronizationInformationType sit = getAggregateSynchronizationInformation(); ActionsExecutedInformationType aeit = getAggregateActionsExecutedInformation(); if (env == null && itit == null && sit == null && aeit == null) { return null; } OperationStatsType rv = new OperationStatsType(); rv.setEnvironmentalPerformanceInformation(env); rv.setIterativeTaskInformation(itit); rv.setSynchronizationInformation(sit); rv.setActionsExecutedInformation(aeit); rv.setTimestamp(XmlTypeConverter.createXMLGregorianCalendar(new Date())); return rv; } @NotNull @Override public List<String> getLastFailures() { return iterativeTaskInformation != null ? iterativeTaskInformation.getLastFailures() : Collections.emptyList(); } private EnvironmentalPerformanceInformationType getAggregateEnvironmentalPerformanceInformation() { if (environmentalPerformanceInformation == null) { return null; } EnvironmentalPerformanceInformationType rv = new EnvironmentalPerformanceInformationType(); EnvironmentalPerformanceInformation.addTo(rv, environmentalPerformanceInformation.getAggregatedValue()); for (Task subtask : getLightweightAsynchronousSubtasks()) { EnvironmentalPerformanceInformation info = ((TaskQuartzImpl) subtask).getEnvironmentalPerformanceInformation(); if (info != null) { EnvironmentalPerformanceInformation.addTo(rv, info.getAggregatedValue()); } } return rv; } private IterativeTaskInformationType getAggregateIterativeTaskInformation() { if (iterativeTaskInformation == null) { return null; } IterativeTaskInformationType rv = new IterativeTaskInformationType(); IterativeTaskInformation.addTo(rv, iterativeTaskInformation.getAggregatedValue(), false); for (Task subtask : getLightweightAsynchronousSubtasks()) { IterativeTaskInformation info = ((TaskQuartzImpl) subtask).getIterativeTaskInformation(); if (info != null) { IterativeTaskInformation.addTo(rv, info.getAggregatedValue(), false); } } return rv; } private SynchronizationInformationType getAggregateSynchronizationInformation() { if (synchronizationInformation == null) { return null; } SynchronizationInformationType rv = new SynchronizationInformationType(); SynchronizationInformation.addTo(rv, synchronizationInformation.getAggregatedValue()); for (Task subtask : getLightweightAsynchronousSubtasks()) { SynchronizationInformation info = ((TaskQuartzImpl) subtask).getSynchronizationInformation(); if (info != null) { SynchronizationInformation.addTo(rv, info.getAggregatedValue()); } } return rv; } private ActionsExecutedInformationType getAggregateActionsExecutedInformation() { if (actionsExecutedInformation == null) { return null; } ActionsExecutedInformationType rv = new ActionsExecutedInformationType(); ActionsExecutedInformation.addTo(rv, actionsExecutedInformation.getAggregatedValue()); for (Task subtask : getLightweightAsynchronousSubtasks()) { ActionsExecutedInformation info = ((TaskQuartzImpl) subtask).getActionsExecutedInformation(); if (info != null) { ActionsExecutedInformation.addTo(rv, info.getAggregatedValue()); } } return rv; } @Override public void recordState(String message) { if (LOGGER.isDebugEnabled()) { // TODO consider this LOGGER.debug("{}", message); } if (PERFORMANCE_ADVISOR.isDebugEnabled()) { PERFORMANCE_ADVISOR.debug("{}", message); } environmentalPerformanceInformation.recordState(message); } @Override public void recordProvisioningOperation(String resourceOid, String resourceName, QName objectClassName, ProvisioningOperation operation, boolean success, int count, long duration) { environmentalPerformanceInformation.recordProvisioningOperation(resourceOid, resourceName, objectClassName, operation, success, count, duration); } @Override public void recordNotificationOperation(String transportName, boolean success, long duration) { environmentalPerformanceInformation.recordNotificationOperation(transportName, success, duration); } @Override public void recordMappingOperation(String objectOid, String objectName, String objectTypeName, String mappingName, long duration) { environmentalPerformanceInformation.recordMappingOperation(objectOid, objectName, objectTypeName, mappingName, duration); } @Override public synchronized void recordSynchronizationOperationEnd(String objectName, String objectDisplayName, QName objectType, String objectOid, long started, Throwable exception, SynchronizationInformation.Record originalStateIncrement, SynchronizationInformation.Record newStateIncrement) { if (synchronizationInformation != null) { synchronizationInformation.recordSynchronizationOperationEnd(objectName, objectDisplayName, objectType, objectOid, started, exception, originalStateIncrement, newStateIncrement); } } @Override public synchronized void recordSynchronizationOperationStart(String objectName, String objectDisplayName, QName objectType, String objectOid) { if (synchronizationInformation != null) { synchronizationInformation.recordSynchronizationOperationStart(objectName, objectDisplayName, objectType, objectOid); } } @Override public synchronized void recordIterativeOperationEnd(String objectName, String objectDisplayName, QName objectType, String objectOid, long started, Throwable exception) { if (iterativeTaskInformation != null) { iterativeTaskInformation.recordOperationEnd(objectName, objectDisplayName, objectType, objectOid, started, exception); } } @Override public void recordIterativeOperationEnd(ShadowType shadow, long started, Throwable exception) { recordIterativeOperationEnd(PolyString.getOrig(shadow.getName()), StatisticsUtil.getDisplayName(shadow), ShadowType.COMPLEX_TYPE, shadow.getOid(), started, exception); } @Override public void recordIterativeOperationStart(ShadowType shadow) { recordIterativeOperationStart(PolyString.getOrig(shadow.getName()), StatisticsUtil.getDisplayName(shadow), ShadowType.COMPLEX_TYPE, shadow.getOid()); } @Override public synchronized void recordIterativeOperationStart(String objectName, String objectDisplayName, QName objectType, String objectOid) { if (iterativeTaskInformation != null) { iterativeTaskInformation.recordOperationStart(objectName, objectDisplayName, objectType, objectOid); } } @Override public void recordObjectActionExecuted(String objectName, String objectDisplayName, QName objectType, String objectOid, ChangeType changeType, String channel, Throwable exception) { if (actionsExecutedInformation != null) { actionsExecutedInformation.recordObjectActionExecuted(objectName, objectDisplayName, objectType, objectOid, changeType, channel, exception); } } @Override public void recordObjectActionExecuted(PrismObject<? extends ObjectType> object, ChangeType changeType, Throwable exception) { recordObjectActionExecuted(object, null, null, changeType, getChannel(), exception); } @Override public <T extends ObjectType> void recordObjectActionExecuted(PrismObject<T> object, Class<T> objectTypeClass, String defaultOid, ChangeType changeType, String channel, Throwable exception) { if (actionsExecutedInformation != null) { String name, displayName, oid; PrismObjectDefinition definition; Class<T> clazz; if (object != null) { name = PolyString.getOrig(object.getName()); displayName = StatisticsUtil.getDisplayName(object); definition = object.getDefinition(); clazz = object.getCompileTimeClass(); oid = object.getOid(); if (oid == null) { // in case of ADD operation oid = defaultOid; } } else { name = null; displayName = null; definition = null; clazz = objectTypeClass; oid = defaultOid; } if (definition == null && clazz != null) { definition = getPrismContext().getSchemaRegistry().findObjectDefinitionByCompileTimeClass(clazz); } QName typeQName; if (definition != null) { typeQName = definition.getTypeName(); } else { typeQName = ObjectType.COMPLEX_TYPE; } actionsExecutedInformation.recordObjectActionExecuted(name, displayName, typeQName, oid, changeType, channel, exception); } } @Override public void markObjectActionExecutedBoundary() { if (actionsExecutedInformation != null) { actionsExecutedInformation.markObjectActionExecutedBoundary(); } } @Override public void resetEnvironmentalPerformanceInformation(EnvironmentalPerformanceInformationType value) { environmentalPerformanceInformation = new EnvironmentalPerformanceInformation(value); } @Override public void resetSynchronizationInformation(SynchronizationInformationType value) { synchronizationInformation = new SynchronizationInformation(value); } @Override public void resetIterativeTaskInformation(IterativeTaskInformationType value) { iterativeTaskInformation = new IterativeTaskInformation(value); } @Override public void resetActionsExecutedInformation(ActionsExecutedInformationType value) { actionsExecutedInformation = new ActionsExecutedInformation(value); } @Override public void startCollectingOperationStatsFromZero(boolean enableIterationStatistics, boolean enableSynchronizationStatistics, boolean enableActionsExecutedStatistics) { resetEnvironmentalPerformanceInformation(null); if (enableIterationStatistics) { resetIterativeTaskInformation(null); } if (enableSynchronizationStatistics) { resetSynchronizationInformation(null); } if (enableActionsExecutedStatistics) { resetActionsExecutedInformation(null); } } @Override public void startCollectingOperationStatsFromStoredValues(boolean enableIterationStatistics, boolean enableSynchronizationStatistics, boolean enableActionsExecutedStatistics) { OperationStatsType stored = getStoredOperationStats(); if (stored == null) { stored = new OperationStatsType(); } resetEnvironmentalPerformanceInformation(stored.getEnvironmentalPerformanceInformation()); if (enableIterationStatistics) { resetIterativeTaskInformation(stored.getIterativeTaskInformation()); } else { iterativeTaskInformation = null; } if (enableSynchronizationStatistics) { resetSynchronizationInformation(stored.getSynchronizationInformation()); } else { synchronizationInformation = null; } if (enableActionsExecutedStatistics) { resetActionsExecutedInformation(stored.getActionsExecutedInformation()); } else { actionsExecutedInformation = null; } } @Override public void storeOperationStats() { try { setOperationStats(getAggregatedLiveOperationStats()); savePendingModifications(new OperationResult(DOT_INTERFACE + ".storeOperationStats")); // TODO fixme } catch (SchemaException|ObjectNotFoundException |ObjectAlreadyExistsException |RuntimeException e) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't store statistical information into task {}", e, this); } } }