/*
* Copyright (c) 2010-2013 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.notifications.impl;
import com.evolveum.midpoint.notifications.api.NotificationManager;
import com.evolveum.midpoint.notifications.api.OperationStatus;
import com.evolveum.midpoint.notifications.api.events.ResourceObjectEvent;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.provisioning.api.ChangeNotificationDispatcher;
import com.evolveum.midpoint.provisioning.api.ResourceOperationDescription;
import com.evolveum.midpoint.provisioning.api.ResourceOperationListener;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
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.ShadowType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* @author mederly
*/
@Component
public class AccountOperationListener implements ResourceOperationListener {
private static final Trace LOGGER = TraceManager.getTrace(AccountOperationListener.class);
private static final String DOT_CLASS = AccountOperationListener.class.getName() + ".";
@Autowired
private LightweightIdentifierGenerator lightweightIdentifierGenerator;
@Autowired
private ChangeNotificationDispatcher provisioningNotificationDispatcher;
@Autowired
private NotificationManager notificationManager;
@Autowired
@Qualifier("cacheRepositoryService")
private transient RepositoryService cacheRepositoryService;
@Autowired
private NotificationFunctionsImpl notificationsUtil;
@PostConstruct
public void init() {
provisioningNotificationDispatcher.registerNotificationListener(this);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Registered account operation notification listener.");
}
}
@Override
public String getName() {
return "user notification account change listener";
}
@Override
public void notifySuccess(ResourceOperationDescription operationDescription, Task task, OperationResult parentResult) {
if (notificationsEnabled()) {
notifyAny(OperationStatus.SUCCESS, operationDescription, task, parentResult.createSubresult(DOT_CLASS + "notifySuccess"));
}
}
private boolean notificationsEnabled() {
if (notificationManager.isDisabled()) {
LOGGER.trace("Notifications are temporarily disabled, exiting the hook.");
return false;
} else {
return true;
}
}
@Override
public void notifyInProgress(ResourceOperationDescription operationDescription, Task task, OperationResult parentResult) {
if (notificationsEnabled()) {
notifyAny(OperationStatus.IN_PROGRESS, operationDescription, task, parentResult.createSubresult(DOT_CLASS + "notifyInProgress"));
}
}
@Override
public void notifyFailure(ResourceOperationDescription operationDescription, Task task, OperationResult parentResult) {
if (notificationsEnabled()) {
notifyAny(OperationStatus.FAILURE, operationDescription, task, parentResult.createSubresult(DOT_CLASS + "notifyFailure"));
}
}
private void notifyAny(OperationStatus status, ResourceOperationDescription operationDescription, Task task, OperationResult result) {
try {
executeNotifyAny(status, operationDescription, task, result);
} catch (RuntimeException e) {
result.recordFatalError("An unexpected exception occurred when preparing and sending notifications: " + e.getMessage(), e);
LoggingUtils.logException(LOGGER, "An unexpected exception occurred when preparing and sending notifications: " + e.getMessage(), e);
}
// todo work correctly with operationResult (in whole notification module)
if (result.isUnknown()) {
result.computeStatus();
}
result.recordSuccessIfUnknown();
// if (LOGGER.isTraceEnabled()) {
// LOGGER.trace("Returning operation result: " + result.dump());
// }
}
private void executeNotifyAny(OperationStatus status, ResourceOperationDescription operationDescription, Task task, OperationResult result) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("AccountOperationListener.notify (" + status + ") called with operationDescription = " + operationDescription.debugDump());
}
if (operationDescription.getObjectDelta() == null) {
LOGGER.warn("Object delta is null, exiting the change listener.");
return;
}
if (operationDescription.getCurrentShadow() == null) {
LOGGER.warn("Current shadow is null, exiting the change listener.");
return;
}
// for the time being, we deal only with accounts here
if (operationDescription.getObjectDelta().getObjectTypeClass() == null ||
!ShadowType.class.isAssignableFrom(operationDescription.getObjectDelta().getObjectTypeClass())) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Object that was changed was not an account, exiting the operation listener (class = " +
operationDescription.getObjectDelta().getObjectTypeClass() + ")");
}
return;
}
ResourceObjectEvent request = createRequest(status, operationDescription, task, result);
notificationManager.processEvent(request, task, result);
}
private ResourceObjectEvent createRequest(OperationStatus status,
ResourceOperationDescription operationDescription,
Task task,
OperationResult result) {
ResourceObjectEvent event = new ResourceObjectEvent(lightweightIdentifierGenerator);
event.setAccountOperationDescription(operationDescription);
event.setOperationStatus(status);
event.setChangeType(operationDescription.getObjectDelta().getChangeType()); // fortunately there's 1:1 mapping
String accountOid = operationDescription.getObjectDelta().getOid();
PrismObject<UserType> user = findRequestee(accountOid, task, result, operationDescription.getObjectDelta().isDelete());
if (user != null) {
event.setRequestee(new SimpleObjectRefImpl(notificationsUtil, user.asObjectable()));
} // otherwise, appropriate messages were already logged
if (task != null && task.getOwner() != null) {
event.setRequester(new SimpleObjectRefImpl(notificationsUtil, task.getOwner()));
} else {
LOGGER.warn("No owner for task " + task + ", therefore no requester will be set for event " + event.getId());
}
if (task != null && task.getChannel() != null) {
event.setChannel(task.getChannel());
} else if (operationDescription.getSourceChannel() != null) {
event.setChannel(operationDescription.getSourceChannel());
}
return event;
}
// private boolean isRequestApplicable(ResourceObjectEvent request, NotificationConfigurationEntryType entry) {
//
// ResourceOperationDescription opDescr = request.getAccountOperationDescription();
// OperationStatus status = request.getOperationStatus();
// ChangeType type = opDescr.getObjectDelta().getChangeType();
// return typeMatches(type, entry.getSituation(), opDescr) && statusMatches(status, entry.getSituation());
// }
private PrismObject<UserType> findRequestee(String accountOid, Task task, OperationResult result, boolean isDelete) {
PrismObject<UserType> user;
if (accountOid != null) {
try {
user = cacheRepositoryService.listAccountShadowOwner(accountOid, result);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("listAccountShadowOwner for account " + accountOid + " yields " + user);
}
} catch (ObjectNotFoundException e) {
LOGGER.trace("There's a problem finding account " + accountOid, e);
return null;
}
if (user != null) {
return user;
}
}
PrismObject<UserType> requestee = task != null ? task.getRequestee() : null;
if (requestee == null) {
LOGGER.warn("There is no owner of account " + accountOid + " (in repo nor in task).");
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Task = " + (task != null ? task.debugDump() : "(null)"));
}
return null;
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Requestee = " + requestee + " for account " + accountOid);
}
if (requestee.getOid() == null) {
return requestee;
}
// let's try to get current value of requestee ... if it exists (it will NOT exist in case of delete operation)
try {
return cacheRepositoryService.getObject(UserType.class, requestee.getOid(), null, result);
} catch (ObjectNotFoundException e) {
if (isDelete) {
result.removeLastSubresult(); // get rid of this error - it's not an error
}
return requestee; // returning last known value
// if (!isDelete) {
// LoggingUtils.logException(LOGGER, "Cannot find owner of account " + accountOid, e);
// } else {
// LOGGER.info("Owner of account " + accountOid + " (user oid " + userOid + ") was probably already deleted.");
// result.removeLastSubresult(); // to suppress the error message (in GUI + in tests)
// }
// return null;
} catch (SchemaException e) {
LoggingUtils.logException(LOGGER, "Cannot find owner of account " + accountOid, e);
return null;
}
}
}