Coverage Report - org.kuali.rice.kew.mail.EmailNode
 
Classes in this File Line Coverage Branch Coverage Complexity
EmailNode
0%
0/77
0%
0/32
5.333
 
 1  
 /*
 2  
  * Copyright 2006-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 
 17  
 package org.kuali.rice.kew.mail;
 18  
 
 19  
 import java.io.StringReader;
 20  
 
 21  
 import javax.xml.parsers.DocumentBuilder;
 22  
 import javax.xml.parsers.DocumentBuilderFactory;
 23  
 import javax.xml.transform.Templates;
 24  
 import javax.xml.transform.TransformerConfigurationException;
 25  
 
 26  
 import org.apache.commons.lang.StringUtils;
 27  
 import org.kuali.rice.core.api.CoreApiServiceLocator;
 28  
 import org.kuali.rice.core.api.config.property.ConfigContext;
 29  
 import org.kuali.rice.core.mail.EmailBody;
 30  
 import org.kuali.rice.core.mail.EmailContent;
 31  
 import org.kuali.rice.core.mail.EmailFrom;
 32  
 import org.kuali.rice.core.mail.EmailSubject;
 33  
 import org.kuali.rice.core.mail.EmailTo;
 34  
 import org.kuali.rice.core.util.xml.XmlHelper;
 35  
 import org.kuali.rice.core.util.xml.XmlJotter;
 36  
 import org.kuali.rice.kew.api.WorkflowRuntimeException;
 37  
 import org.kuali.rice.kew.dto.DTOConverter;
 38  
 import org.kuali.rice.kew.dto.RouteHeaderDTO;
 39  
 import org.kuali.rice.kew.dto.RouteNodeInstanceDTO;
 40  
 import org.kuali.rice.kew.engine.RouteContext;
 41  
 import org.kuali.rice.kew.engine.RouteHelper;
 42  
 import org.kuali.rice.kew.engine.node.SimpleNode;
 43  
 import org.kuali.rice.kew.engine.node.SimpleResult;
 44  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 45  
 import org.kuali.rice.kew.util.KEWConstants;
 46  
 import org.kuali.rice.kim.bo.Person;
 47  
 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
 48  
 import org.w3c.dom.Document;
 49  
 import org.w3c.dom.Element;
 50  
 import org.w3c.dom.NodeList;
 51  
 import org.xml.sax.InputSource;
 52  
 
 53  
 import com.thoughtworks.xstream.XStream;
 54  
 
 55  
 
 56  
 /**
 57  
  * A node which will send emails using the configured stylesheet to generate the email content.
 58  
  *
 59  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 60  
  */
 61  0
 public class EmailNode implements SimpleNode {
 62  
 
 63  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(EmailNode.class);
 64  
 
 65  0
     private EmailStyleHelper emailStyleHelper = new EmailStyleHelper();
 66  
     private String styleName;
 67  
     private String from;
 68  
     private String to;
 69  
     
 70  
     public SimpleResult process(RouteContext context, RouteHelper helper) throws Exception {
 71  0
             if (context.isSimulation()) {
 72  0
             if (!context.getActivationContext().isActivateRequests()) {
 73  0
                     return new SimpleResult(true);
 74  
             }
 75  
         } 
 76  0
         loadConfiguration(context);
 77  0
         Document document = generateXmlInput(context);
 78  0
         if (LOG.isDebugEnabled()) {
 79  0
             LOG.debug("XML input for email tranformation:\n" + XmlJotter.jotNode(document));
 80  
         }
 81  0
         Templates style = loadStyleSheet(styleName);
 82  0
         EmailContent emailContent = emailStyleHelper.generateEmailContent(style, document);
 83  0
         if (!StringUtils.isBlank(to)) {
 84  0
                 KEWServiceLocator.getMailer().sendEmail(new EmailFrom(from), new EmailTo(to), new EmailSubject(emailContent.getSubject()), new EmailBody(emailContent.getBody()), emailContent.isHtml());
 85  
         }
 86  0
         return new SimpleResult(true);
 87  
     }
 88  
 
 89  
     protected Document generateXmlInput(RouteContext context) throws Exception {
 90  0
         DocumentBuilder db = getDocumentBuilder(true);
 91  0
         Document doc = db.newDocument();
 92  0
         Element emailNodeElem = doc.createElement("emailNode");
 93  0
         doc.appendChild(emailNodeElem);
 94  0
         String principalId = null;  // Added to the convertRouteHeader is not ambigious.
 95  0
         RouteHeaderDTO routeHeaderVO = DTOConverter.convertRouteHeader(context.getDocument(), principalId);
 96  0
         RouteNodeInstanceDTO routeNodeInstanceVO = DTOConverter.convertRouteNodeInstance(context.getNodeInstance());
 97  0
         Document documentContent = context.getDocumentContent().getDocument();
 98  0
         XStream xstream = new XStream();
 99  0
         Element docElem = XmlHelper.readXml(xstream.toXML(routeHeaderVO)).getDocumentElement();
 100  0
         Element nodeElem = XmlHelper.readXml(xstream.toXML(routeNodeInstanceVO)).getDocumentElement();
 101  0
         emailNodeElem.appendChild(doc.importNode(docElem, true));
 102  0
         emailNodeElem.appendChild(doc.importNode(nodeElem, true));
 103  0
         emailNodeElem.appendChild(doc.importNode(documentContent.getDocumentElement(), true));
 104  0
         Element dConElem = context.getDocumentContent().getApplicationContent();//Add document Content element for
 105  0
                   emailNodeElem.appendChild(doc.importNode(dConElem, true));//access by the stylesheet when creating the email
 106  0
         return doc;
 107  
     }
 108  
 
 109  
     protected DocumentBuilder getDocumentBuilder(boolean coalesce) throws Exception {
 110  0
         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 111  0
         dbf.setCoalescing(coalesce);
 112  0
         return dbf.newDocumentBuilder();
 113  
     }
 114  
 
 115  
     protected Templates loadStyleSheet(String styleName) {
 116  
         try {
 117  0
             Templates style = CoreApiServiceLocator.getStyleService().getStyleAsTranslet(styleName);
 118  0
             if (style == null) {
 119  0
                 throw new WorkflowRuntimeException("Failed to locate stylesheet with name '" + styleName + "'");
 120  
             }
 121  0
             return style;
 122  0
         } catch (TransformerConfigurationException tce) {
 123  0
             throw new WorkflowRuntimeException("Failed to load stylesheet with name '" + styleName + "'");
 124  
         }
 125  
     }
 126  
 
 127  
     protected boolean isProduction() {
 128  0
         return ConfigContext.getCurrentContextConfig().getProperty(KEWConstants.PROD_DEPLOYMENT_CODE).equalsIgnoreCase(
 129  
                         ConfigContext.getCurrentContextConfig().getEnvironment());
 130  
     }
 131  
 
 132  
     protected void loadConfiguration(RouteContext context) throws Exception {
 133  0
         String contentFragment = context.getNodeInstance().getRouteNode().getContentFragment();
 134  0
         DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
 135  0
         Document document = db.parse(new InputSource(new StringReader(contentFragment)));
 136  0
         if (!isProduction()) {
 137  0
             NodeList testAddresses = document.getElementsByTagName("testAddress");
 138  0
             if (testAddresses.getLength() >= 1) {
 139  0
                 this.to = testAddresses.item(0).getTextContent();
 140  
             }
 141  0
         } else {
 142  0
             NodeList toAddresses = document.getElementsByTagName("to");
 143  0
             if (toAddresses.getLength() != 1) {
 144  0
                 throw new WorkflowRuntimeException("Must have exactly one 'to' address");
 145  
             }
 146  0
             to = toAddresses.item(0).getTextContent();
 147  0
             if ("initiator".equalsIgnoreCase(to))
 148  
             {        
 149  0
                     Person person = KimApiServiceLocator.getPersonService().getPerson(context.getDocument().getInitiatorWorkflowId());
 150  0
                         to = (person == null ? "" : person.getEmailAddressUnmasked());
 151  
             }
 152  0
             if (StringUtils.isBlank(to)) {
 153  0
                     throw new WorkflowRuntimeException("Email Address is missing from user's profile.");
 154  
             }
 155  
         }
 156  
 
 157  0
         NodeList fromAddresses = document.getElementsByTagName("from");
 158  0
         if (fromAddresses.getLength() != 1) {
 159  0
             throw new WorkflowRuntimeException("Must have exactly one 'from' address");
 160  
         }
 161  0
         this.from = fromAddresses.item(0).getTextContent();
 162  
 
 163  0
         if ("initiator".equalsIgnoreCase(this.from)) {
 164  0
                 Person initiator = KEWServiceLocator.getIdentityHelperService().getPerson(context.getDocument().getInitiatorWorkflowId());
 165  
                 // contructs the email from so that it includes name as well as address
 166  
                 // for example: "Doe, John D" <john@doe.com>
 167  0
                   this.from = "\"" + initiator.getName() + "\" <";
 168  0
                   this.from += initiator.getEmailAddress() + ">";
 169  
         }
 170  0
         if (StringUtils.isBlank(this.from)) {
 171  0
                 throw new WorkflowRuntimeException("No email address could be found found for principal with id " + context.getDocument().getInitiatorWorkflowId());
 172  
         }
 173  
         
 174  0
         if (LOG.isInfoEnabled()) {
 175  0
                   LOG.info("Email From is set to:" + this.from);
 176  0
                   LOG.info("Email To is set to:" + this.to);
 177  
         }
 178  
         
 179  0
         NodeList styleNames = document.getElementsByTagName("style");
 180  0
         if (styleNames.getLength() != 1) {
 181  0
             throw new WorkflowRuntimeException("Must have exactly one 'style'");
 182  
         }
 183  0
         this.styleName = styleNames.item(0).getTextContent();
 184  0
     }
 185  
 
 186  
 
 187  
 
 188  
 
 189  
 }