View Javadoc

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