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;
	}
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);
	
		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) {
				
					try {
						LOGGER.trace("Re-fetching object {} because it is not in the change", change.getIdentifiers());
						currentShadow = fetchResourceObject(shadowCtx, 
								change.getIdentifiers(), shadowAttrsToReturn, true, parentResult);
						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());
					
						iterator.remove();
						continue;
					}
				} else {
					if (ctx.isWildcard()) {
						if (!MiscUtil.equals(shadowAttrsToReturn, attrsToReturn)) {
						
							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;
	}
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); 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) { try { logger.trace("re-fetching object {} because it is not in the change", change.getidentifiers()); currentshadow = fetchresourceobject(shadowctx, change.getidentifiers(), shadowattrstoreturn, true, parentresult); 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()); iterator.remove(); continue; } } else { if (ctx.iswildcard()) { if (!miscutil.equals(shadowattrstoreturn, attrstoreturn)) { 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; }