// re-fetch the shadow if necessary (if attributesToGet does not match)
public List<Change> fetchChanges(ProvisioningContext ctx, PrismProperty<?> lastToken,
			OperationResult parentResult) throws SchemaException,
			CommunicationException, ConfigurationException, SecurityViolationException, GenericFrameworkException, ObjectNotFoundException, ExpressionEvaluationException {
		Validate.notNull(parentResult, "Operation result must not be null.");
		LOGGER.trace("START fetch changes, objectClass: {}", ctx.getObjectClassDefinition());
		AttributesToReturn attrsToReturn = null;
		if (!ctx.isWildcard()) {
			attrsToReturn = ProvisioningUtil.createAttributesToReturn(ctx);
		}
		ConnectorInstance connector = ctx.getConnector(LiveSyncCapabilityType.class, parentResult);
		// get changes from the connector
		List<Change> changes = connector.fetchChanges(ctx.getObjectClassDefinition(), lastToken, attrsToReturn, ctx, parentResult);
		Iterator<Change> iterator = changes.iterator();
		while (iterator.hasNext()) {
			Change change = iterator.next();
			LOGGER.trace("Original change:\n{}", change.debugDump());
			if (change.isTokenOnly()) {
				continue;
			}
			ProvisioningContext shadowCtx = ctx;
			AttributesToReturn shadowAttrsToReturn = attrsToReturn;
			PrismObject<ShadowType> currentShadow = change.getCurrentShadow();
			ObjectClassComplexTypeDefinition changeObjectClassDefinition = change.getObjectClassDefinition();
			if (changeObjectClassDefinition == null) {
				if (!ctx.isWildcard() || change.getObjectDelta() == null || !change.getObjectDelta().isDelete()) {
					throw new SchemaException("No object class definition in change "+change);
				}
			}
			if (ctx.isWildcard() && changeObjectClassDefinition != null) {
				shadowCtx = ctx.spawn(changeObjectClassDefinition.getTypeName());
				if (shadowCtx.isWildcard()) {
					String message = "Unkown object class "+changeObjectClassDefinition.getTypeName()+" found in synchronization delta";
					parentResult.recordFatalError(message);
					throw new SchemaException(message);
				}
				change.setObjectClassDefinition(shadowCtx.getObjectClassDefinition());
				shadowAttrsToReturn = ProvisioningUtil.createAttributesToReturn(shadowCtx);
			}
			if (change.getObjectDelta() == null || !change.getObjectDelta().isDelete()) {
				if (currentShadow == null) {
					// There is no current shadow in a change. Add it by fetching it explicitly.
					try {
						LOGGER.trace("Re-fetching object {} because it is not in the change", change.getIdentifiers());
						currentShadow = fetchResourceObject(shadowCtx, 
								change.getIdentifiers(), shadowAttrsToReturn, true, parentResult);	// todo consider whether it is always necessary to fetch the entitlements
						change.setCurrentShadow(currentShadow);
					} catch (ObjectNotFoundException ex) {
						parentResult.recordHandledError(
								"Object detected in change log no longer exist on the resource. Skipping processing this object.", ex);
						LOGGER.warn("Object detected in change log no longer exist on the resource. Skipping processing this object "
								+ ex.getMessage());
						// TODO: Maybe change to DELETE instead of this?
						iterator.remove();
						continue;
					}
				} else {
					if (ctx.isWildcard()) {
						if (!MiscUtil.equals(shadowAttrsToReturn, attrsToReturn)) {
							// re-fetch the shadow if necessary (if attributesToGet does not match)
							ResourceObjectIdentification identification = ResourceObjectIdentification.create(shadowCtx.getObjectClassDefinition(), 
									change.getIdentifiers());
							identification.validatePrimaryIdenfiers();
							LOGGER.trace("Re-fetching object {} because of attrsToReturn", identification);
							currentShadow = connector.fetchObject(identification, shadowAttrsToReturn, ctx, parentResult);
						}
					}
					PrismObject<ShadowType> processedCurrentShadow = postProcessResourceObjectRead(shadowCtx,
							currentShadow, true, parentResult);
					change.setCurrentShadow(processedCurrentShadow);
				}
			}
			LOGGER.trace("Processed change\n:{}", change.debugDump());
		}
		computeResultStatus(parentResult);
		LOGGER.trace("END fetch changes ({} changes)", changes == null ? "null" : changes.size());
		return changes;
	}
if (ctx.isWildcard()) {
if (!MiscUtil.equals(shadowAttrsToReturn, attrsToReturn)) {
// re-fetch the shadow if necessary (if attributesToGet does not match)
ResourceObjectIdentification identification = ResourceObjectIdentification.create(shadowCtx.getObjectClassDefinition(),
change.getIdentifiers());
"Object detected in change log no longer exist on the resource. Skipping processing this object.", ex);
LOGGER.warn("Object detected in change log no longer exist on the resource. Skipping processing this object "
+ ex.getMessage());
// TODO: Maybe change to DELETE instead of this?
iterator.remove();
continue;
}
} else {
if (ctx.isWildcard()) {
if (!MiscUtil.equals(shadowAttrsToReturn, attrsToReturn)) {
// re-fetch the shadow if necessary (if attributesToGet does not match)
ResourceObjectIdentification identification = ResourceObjectIdentification.create(shadowCtx.getObjectClassDefinition(),
change.getIdentifiers());
identification.validatePrimaryIdenfiers();
LOGGER.trace("Re-fetching object {} because of attrsToReturn", identification);
currentShadow = connector.fetchObject(identification, shadowAttrsToReturn, ctx, parentResult);
}
}
PrismObject<ShadowType> processedCurrentShadow = postProcessResourceObjectRead(shadowCtx,
currentShadow, true, parentResult);
change.setCurrentShadow(processedCurrentShadow);
if (change.getObjectDelta() == null || !change.getObjectDelta().isDelete()) {
if (currentShadow == null) {
// There is no current shadow in a change. Add it by fetching it explicitly.
try {
LOGGER.trace("Re-fetching object {} because it is not in the change", change.getIdentifiers());
currentShadow = fetchResourceObject(shadowCtx,
change.getIdentifiers(), shadowAttrsToReturn, true, parentResult); // todo consider whether it is always necessary to fetch the entitlements
change.setCurrentShadow(currentShadow);
} catch (ObjectNotFoundException ex) {
parentResult.recordHandledError(
"Object detected in change log no longer exist on the resource. Skipping processing this object.", ex);
LOGGER.warn("Object detected in change log no longer exist on the resource. Skipping processing this object "
+ ex.getMessage());
// TODO: Maybe change to DELETE instead of this?
iterator.remove();
continue;
}
} else {
if (ctx.isWildcard()) {
if (!MiscUtil.equals(shadowAttrsToReturn, attrsToReturn)) {
// re-fetch the shadow if necessary (if attributesToGet does not match)
ResourceObjectIdentification identification = ResourceObjectIdentification.create(shadowCtx.getObjectClassDefinition(),
change.getIdentifiers());
identification.validatePrimaryIdenfiers();
LOGGER.trace("Re-fetching object {} because of attrsToReturn", identification);
currentShadow = connector.fetchObject(identification, shadowAttrsToReturn, ctx, parentResult);
}
}
PrismObject<ShadowType> processedCurrentShadow = postProcessResourceObjectRead(shadowCtx,
currentShadow, true, parentResult);
change.setCurrentShadow(processedCurrentShadow);
}
}
LOGGER.trace("Processed change\n:{}", change.debugDump());
}
computeResultStatus(parentResult);
LOGGER.trace("END fetch changes ({} changes)", changes == null ? "null" : changes.size());
return changes;
}