/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you under the Educational * Community 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://opensource.org/licenses/ecl2.txt * * 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 org.opencastproject.email.template.impl; import org.opencastproject.email.template.api.EmailTemplateService; import org.opencastproject.job.api.Incident; import org.opencastproject.job.api.IncidentTree; import org.opencastproject.mediapackage.Catalog; import org.opencastproject.mediapackage.EName; import org.opencastproject.mediapackage.MediaPackage; import org.opencastproject.metadata.dublincore.DublinCore; import org.opencastproject.metadata.dublincore.DublinCoreCatalog; import org.opencastproject.metadata.dublincore.DublinCores; import org.opencastproject.serviceregistry.api.IncidentService; import org.opencastproject.util.doc.DocUtil; import org.opencastproject.workflow.api.WorkflowInstance; import org.opencastproject.workflow.api.WorkflowOperationInstance; import org.opencastproject.workflow.api.WorkflowOperationInstance.OperationState; import org.opencastproject.workspace.api.Workspace; import org.apache.commons.io.IOUtils; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; public class EmailTemplateServiceImpl implements EmailTemplateService { private static final Logger logger = LoggerFactory.getLogger(EmailTemplateServiceImpl.class); public static final String DEFAULT_DELIMITER_FOR_MULTIPLE = ","; /** The workspace (needed to read the catalogs when processing templates) **/ private Workspace workspace; /** Email template scanner is optional and has dynamic policy */ private final AtomicReference<EmailTemplateScanner> templateScannerRef = new AtomicReference<EmailTemplateScanner>(); /** The incident service (to list errors in email) */ private IncidentService incidentService = null; protected void activate(ComponentContext context) { logger.info("EmailTemplateServiceImpl activated"); } /** * Apply the template to the workflow instance. * * @param templateName * template name * @param templateContent * template content * @param workflowInstance * workflow * @return text with applied template */ @Override public String applyTemplate(String templateName, String templateContent, WorkflowInstance workflowInstance) { if (templateContent == null && templateScannerRef.get() != null) { templateContent = templateScannerRef.get().getTemplate(templateName); } if (templateContent == null) { logger.warn("E-mail template not found: {}", templateName); return "TEMPLATE NOT FOUND: " + templateName; // it's probably missing } // Build email data structure and apply the template HashMap<String, HashMap<String, String>> catalogs = initCatalogs(workflowInstance.getMediaPackage()); WorkflowOperationInstance failed = findFailedOperation(workflowInstance); List<Incident> incidentList = null; if (failed != null) { try { IncidentTree incidents = incidentService.getIncidentsOfJob(failed.getId(), true); incidentList = generateIncidentList(incidents); } catch (Exception e) { logger.error("Error when populating template with incidents", e); // Incidents in email will be empty } } return DocUtil.generate(new EmailData(templateName, workflowInstance, catalogs, failed, incidentList), templateContent); } /** * Initializes the map with all fields from the dublin core catalogs. */ private HashMap<String, HashMap<String, String>> initCatalogs(MediaPackage mediaPackage) { HashMap<String, HashMap<String, String>> catalogs = new HashMap<String, HashMap<String, String>>(); Catalog[] dcs = mediaPackage.getCatalogs(DublinCoreCatalog.ANY_DUBLINCORE); for (int i = 0; dcs != null && i < dcs.length; i++) { DublinCoreCatalog dc = null; InputStream in = null; try { File f = workspace.get(dcs[i].getURI()); in = new FileInputStream(f); dc = DublinCores.read(in); } catch (Exception e) { logger.warn("Error when populating catalog data", e); // Don't include the info continue; } finally { IOUtils.closeQuietly(in); } if (dc != null) { String catalogFlavor = dcs[i].getFlavor().getSubtype(); HashMap<String, String> catalogHash = new HashMap<String, String>(); for (EName ename : dc.getProperties()) { String name = ename.getLocalName(); catalogHash.put(name, dc.getAsText(ename, DublinCore.LANGUAGE_ANY, DEFAULT_DELIMITER_FOR_MULTIPLE)); } catalogs.put(catalogFlavor, catalogHash); } } return catalogs; } /** * Traverses the workflow until it finds a failed operation that has failOnError=true * * @param workflow * @return the workflow operation that failed */ private WorkflowOperationInstance findFailedOperation(WorkflowInstance workflow) { ArrayList<WorkflowOperationInstance> operations = new ArrayList<WorkflowOperationInstance>(workflow.getOperations()); // Current operation is the email operation WorkflowOperationInstance emailOp = workflow.getCurrentOperation(); // Look for the last operation that is in failed state and has failOnError true int i = operations.indexOf(emailOp) - 1; WorkflowOperationInstance op = null; for (; i >= 0; i--) { op = operations.get(i); if (OperationState.FAILED.equals(op.getState()) && op.isFailWorkflowOnException()) { return op; } } return null; } /** * Generates list of all incidents in the tree * * @param tree * the incident tree * @return a flat list of incidents */ private List<Incident> generateIncidentList(IncidentTree tree) { List<Incident> list = new LinkedList<Incident>(); if (tree != null && tree.getDescendants() != null && tree.getDescendants().size() > 0) { for (IncidentTree subtree : tree.getDescendants()) { list.addAll(generateIncidentList(subtree)); } } list.addAll(tree.getIncidents()); return list; } /** * Callback for OSGi to set the {@link Workspace}. * * @param ws * the workspace */ void setWorkspace(Workspace ws) { this.workspace = ws; } /** * Callback for OSGi to set the {@link EmailTemplateScanner}. * * @param templateScanner * the template scanner service */ void setEmailTemplateScanner(EmailTemplateScanner templateScanner) { this.templateScannerRef.compareAndSet(null, templateScanner); } /** * Callback for OSGi to unset the {@link EmailTemplateScanner}. * * @param templateScanner * the template scanner service */ void unsetEmailTemplateScanner(EmailTemplateScanner templateScanner) { this.templateScannerRef.compareAndSet(templateScanner, null); } /** * Callback for OSGi to unset the {@link IncidentService}. * * @param incidentService * the incident service */ public void setIncidentService(IncidentService incidentService) { this.incidentService = incidentService; } }