/* * Copyright (c) 2010-2017 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.notifiers; import com.evolveum.midpoint.model.api.ProgressInformation; import com.evolveum.midpoint.model.common.expression.ExpressionVariables; import com.evolveum.midpoint.notifications.api.NotificationManager; import com.evolveum.midpoint.notifications.api.events.Event; import com.evolveum.midpoint.notifications.api.events.ModelEvent; import com.evolveum.midpoint.notifications.api.events.SimpleObjectRef; import com.evolveum.midpoint.notifications.api.transports.Message; import com.evolveum.midpoint.notifications.api.transports.Transport; import com.evolveum.midpoint.notifications.impl.NotificationFunctionsImpl; import com.evolveum.midpoint.notifications.impl.formatters.TextFormatter; import com.evolveum.midpoint.notifications.impl.handlers.AggregatedEventHandler; import com.evolveum.midpoint.notifications.impl.handlers.BaseHandler; import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.path.ItemPathSegment; import com.evolveum.midpoint.prism.path.NameItemPathSegment; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.SchemaException; 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 org.apache.cxf.common.util.StringUtils; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import static com.evolveum.midpoint.model.api.ProgressInformation.ActivityType.NOTIFICATIONS; import static com.evolveum.midpoint.model.api.ProgressInformation.StateType.ENTERING; /** * @author mederly */ @Component public class GeneralNotifier extends BaseHandler { private static final Trace DEFAULT_LOGGER = TraceManager.getTrace(GeneralNotifier.class); @Autowired protected NotificationManager notificationManager; @Autowired protected NotificationFunctionsImpl functions; @Autowired protected TextFormatter textFormatter; @Autowired protected AggregatedEventHandler aggregatedEventHandler; @PostConstruct public void init() { register(GeneralNotifierType.class); } @Override public boolean processEvent(Event event, EventHandlerType eventHandlerType, NotificationManager notificationManager, Task task, OperationResult parentResult) throws SchemaException { OperationResult result = parentResult.createSubresult(GeneralNotifier.class.getName() + ".processEvent"); logStart(getLogger(), event, eventHandlerType); boolean applies = aggregatedEventHandler.processEvent(event, eventHandlerType, notificationManager, task, result); if (applies) { GeneralNotifierType generalNotifierType = (GeneralNotifierType) eventHandlerType; if (!quickCheckApplicability(event, generalNotifierType, result)) { // nothing to do -- an appropriate message has to be logged in quickCheckApplicability method } else { if (!checkApplicability(event, generalNotifierType, result)) { // nothing to do -- an appropriate message has to be logged in checkApplicability method } else if (generalNotifierType.getTransport().isEmpty()) { getLogger().warn("No transports for this notifier, exiting without sending any notifications."); } else { ExpressionVariables variables = getDefaultVariables(event, result); if (event instanceof ModelEvent) { ((ModelEvent) event).getModelContext().reportProgress( new ProgressInformation(NOTIFICATIONS, ENTERING)); } try { for (String transportName : generalNotifierType.getTransport()) { variables.addVariableDefinition(SchemaConstants.C_TRANSPORT_NAME, transportName); Transport transport = notificationManager.getTransport(transportName); List<String> recipientsAddresses = getRecipientsAddresses(event, generalNotifierType, variables, getDefaultRecipient(event, generalNotifierType, result), transportName, transport, task, result); if (!recipientsAddresses.isEmpty()) { String body = getBodyFromExpression(event, generalNotifierType, variables, task, result); String subject = getSubjectFromExpression(event, generalNotifierType, variables, task, result); String from = getFromFromExpression(event, generalNotifierType, variables, task, result); String contentType = getContentTypeFromExpression(event, generalNotifierType, variables, task, result); if (body == null) { body = getBody(event, generalNotifierType, transportName, task, result); } if (subject == null) { subject = generalNotifierType.getSubjectPrefix() != null ? generalNotifierType.getSubjectPrefix() : ""; subject += getSubject(event, generalNotifierType, transportName, task, result); } Message message = new Message(); message.setBody(body != null ? body : ""); if (contentType != null) { message.setContentType(contentType); } else if (generalNotifierType.getContentType() != null) { message.setContentType(generalNotifierType.getContentType()); } message.setSubject(subject); if (from != null) { message.setFrom(from); } message.setTo(recipientsAddresses); message.setCc(getCcBccAddresses(generalNotifierType.getCcExpression(), variables, "notification cc-expression", task, result)); message.setBcc(getCcBccAddresses(generalNotifierType.getBccExpression(), variables, "notification bcc-expression", task, result)); getLogger().trace("Sending notification via transport {}:\n{}", transportName, message); transport.send(message, transportName, event, task, result); } else { getLogger().info("No recipients addresses for transport " + transportName + ", message corresponding to event " + event.getId() + " will not be send."); } } } finally { if (event instanceof ModelEvent) { ((ModelEvent) event).getModelContext().reportProgress( new ProgressInformation(NOTIFICATIONS, result)); } } } } } logEnd(getLogger(), event, eventHandlerType, applies); result.computeStatusIfUnknown(); return true; // not-applicable notifiers do not stop processing of other notifiers } protected boolean quickCheckApplicability(Event event, GeneralNotifierType generalNotifierType, OperationResult result) { return true; } protected boolean checkApplicability(Event event, GeneralNotifierType generalNotifierType, OperationResult result) { return true; } protected String getSubject(Event event, GeneralNotifierType generalNotifierType, String transport, Task task, OperationResult result) { return null; } protected String getBody(Event event, GeneralNotifierType generalNotifierType, String transport, Task task, OperationResult result) throws SchemaException { return null; } protected UserType getDefaultRecipient(Event event, GeneralNotifierType generalNotifierType, OperationResult result) { ObjectType objectType = functions.getObjectType(event.getRequestee(), true, result); if (objectType instanceof UserType) { return (UserType) objectType; } else { return null; } } protected Trace getLogger() { return DEFAULT_LOGGER; // in case a subclass does not provide its own logger } protected List<String> getRecipientsAddresses(Event event, GeneralNotifierType generalNotifierType, ExpressionVariables variables, UserType defaultRecipient, String transportName, Transport transport, Task task, OperationResult result) { List<String> addresses = new ArrayList<>(); if (!generalNotifierType.getRecipientExpression().isEmpty()) { for (ExpressionType expressionType : generalNotifierType.getRecipientExpression()) { List<String> r = evaluateExpressionChecked(expressionType, variables, "notification recipient", task, result); if (r != null) { addresses.addAll(r); } } if (addresses.isEmpty()) { getLogger().info("Notification for " + event + " will not be sent, because there are no known recipients."); } } else if (defaultRecipient == null) { getLogger().info("Unknown default recipient, notification will not be sent."); } else { String address = transport.getDefaultRecipientAddress(defaultRecipient); if (StringUtils.isEmpty(address)) { getLogger().info("Notification to " + defaultRecipient.getName() + " will not be sent, because the user has no address (mail, phone number, etc) for transport '" + transportName + "' set."); } else { addresses.add(address); } } return addresses; } @NotNull protected List<String> getCcBccAddresses(List<ExpressionType> expressions, ExpressionVariables variables, String shortDesc, Task task, OperationResult result) { List<String> addresses = new ArrayList<>(); for (ExpressionType expressionType : expressions) { List<String> r = evaluateExpressionChecked(expressionType, variables, shortDesc, task, result); if (r != null) { addresses.addAll(r); } } return addresses; } protected String getSubjectFromExpression(Event event, GeneralNotifierType generalNotifierType, ExpressionVariables variables, Task task, OperationResult result) { if (generalNotifierType.getSubjectExpression() != null) { List<String> subjectList = evaluateExpressionChecked(generalNotifierType.getSubjectExpression(), variables, "subject expression", task, result); if (subjectList == null || subjectList.isEmpty()) { getLogger().warn("Subject expression for event " + event.getId() + " returned nothing."); return ""; } if (subjectList.size() > 1) { getLogger().warn("Subject expression for event " + event.getId() + " returned more than 1 item."); } return subjectList.get(0); } else { return null; } } protected String getFromFromExpression(Event event, GeneralNotifierType generalNotifierType, ExpressionVariables variables, Task task, OperationResult result) { if (generalNotifierType.getFromExpression() != null) { List<String> fromList = evaluateExpressionChecked(generalNotifierType.getFromExpression(), variables, "from expression", task, result); if (fromList == null || fromList.isEmpty()) { getLogger().info("from expression for event " + event.getId() + " returned nothing."); return null; } if (fromList.size() > 1) { getLogger().warn("from expression for event " + event.getId() + " returned more than 1 item."); } return fromList.get(0); } else { return null; } } protected String getContentTypeFromExpression(Event event, GeneralNotifierType generalNotifierType, ExpressionVariables variables, Task task, OperationResult result) { if (generalNotifierType.getContentTypeExpression() != null) { List<String> contentTypeList = evaluateExpressionChecked(generalNotifierType.getContentTypeExpression(), variables, "contentType expression", task, result); if (contentTypeList == null || contentTypeList.isEmpty()) { getLogger().info("contentType expression for event " + event.getId() + " returned nothing."); return null; } if (contentTypeList.size() > 1) { getLogger().warn("contentType expression for event " + event.getId() + " returned more than 1 item."); } return contentTypeList.get(0); } else { return null; } } protected String getBodyFromExpression(Event event, GeneralNotifierType generalNotifierType, ExpressionVariables variables, Task task, OperationResult result) { if (generalNotifierType.getBodyExpression() != null) { List<String> bodyList = evaluateExpressionChecked(generalNotifierType.getBodyExpression(), variables, "body expression", task, result); if (bodyList == null || bodyList.isEmpty()) { getLogger().warn("Body expression for event " + event.getId() + " returned nothing."); return ""; } StringBuilder body = new StringBuilder(); for (String s : bodyList) { body.append(s); } return body.toString(); } else { return null; } } // TODO implement more efficiently // precondition: delta is MODIFY delta protected boolean deltaContainsOtherPathsThan(ObjectDelta<? extends ObjectType> delta, List<ItemPath> paths) { for (ItemDelta itemDelta : delta.getModifications()) { if (!NotificationFunctionsImpl.isAmongHiddenPaths(itemDelta.getPath(), paths)) { return true; } } return false; } protected boolean isWatchAuxiliaryAttributes(GeneralNotifierType generalNotifierType) { return Boolean.TRUE.equals((generalNotifierType).isWatchAuxiliaryAttributes()); } protected void appendModifications(StringBuilder body, ObjectDelta<? extends ObjectType> delta, List<ItemPath> hiddenPaths, Boolean showValuesBoolean) { boolean showValues = !Boolean.FALSE.equals(showValuesBoolean); for (ItemDelta<?,?> itemDelta : delta.getModifications()) { if (NotificationFunctionsImpl.isAmongHiddenPaths(itemDelta.getPath(), hiddenPaths)) { continue; } body.append(" - "); body.append(formatPath(itemDelta)); if (showValues) { body.append(":\n"); if (itemDelta.isAdd()) { for (PrismValue prismValue : itemDelta.getValuesToAdd()) { body.append(" --- ADD: "); body.append(prismValue.debugDump(2)); body.append("\n"); } } if (itemDelta.isDelete()) { for (PrismValue prismValue : itemDelta.getValuesToDelete()) { body.append(" --- DELETE: "); body.append(prismValue.debugDump(2)); body.append("\n"); } } if (itemDelta.isReplace()) { for (PrismValue prismValue : itemDelta.getValuesToReplace()) { body.append(" --- REPLACE: "); body.append(prismValue.debugDump(2)); body.append("\n"); } } } else { body.append("\n"); } } } private String formatPath(ItemDelta itemDelta) { if (itemDelta.getDefinition() != null && itemDelta.getDefinition().getDisplayName() != null) { return itemDelta.getDefinition().getDisplayName(); } StringBuilder sb = new StringBuilder(); for (ItemPathSegment itemPathSegment : itemDelta.getPath().getSegments()) { if (itemPathSegment instanceof NameItemPathSegment) { NameItemPathSegment nameItemPathSegment = (NameItemPathSegment) itemPathSegment; if (sb.length() > 0) { sb.append("/"); } sb.append(nameItemPathSegment.getName().getLocalPart()); } } return sb.toString(); } @Override protected ExpressionVariables getDefaultVariables(Event event, OperationResult result) { ExpressionVariables variables = super.getDefaultVariables(event, result); variables.addVariableDefinition(SchemaConstants.C_TEXT_FORMATTER, textFormatter); return variables; } public String formatRequester(Event event, OperationResult result) { SimpleObjectRef requesterRef = event.getRequester(); if (requesterRef == null) { return "(unknown or none)"; } ObjectType requester = requesterRef.resolveObjectType(result, false); String name = PolyString.getOrig(requester.getName()); if (requester instanceof UserType) { return name + " (" + PolyString.getOrig(((UserType) requester).getFullName()) + ")"; } else { return name; } } }