KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > content > email > NotificationServices


1 /*
2  * $Id: NotificationServices.java 6642 2006-02-01 02:29:51Z sichen $
3  *
4  * Copyright (c) 2003 The Open For Business Project - www.ofbiz.org
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  */

25 package org.ofbiz.content.email;
26
27 import java.io.IOException JavaDoc;
28 import java.io.InputStreamReader JavaDoc;
29 import java.io.StringWriter JavaDoc;
30 import java.io.Writer JavaDoc;
31 import java.net.InetAddress JavaDoc;
32 import java.net.URL JavaDoc;
33 import java.net.UnknownHostException JavaDoc;
34 import java.util.HashMap JavaDoc;
35 import java.util.Map JavaDoc;
36
37 import freemarker.template.TemplateException;
38
39 import org.ofbiz.base.util.Debug;
40 import org.ofbiz.base.util.UtilMisc;
41 import org.ofbiz.base.util.UtilProperties;
42 import org.ofbiz.base.util.UtilURL;
43 import org.ofbiz.base.util.UtilValidate;
44 import org.ofbiz.base.util.template.FreeMarkerWorker;
45 import org.ofbiz.entity.GenericDelegator;
46 import org.ofbiz.entity.GenericEntityException;
47 import org.ofbiz.entity.GenericValue;
48 import org.ofbiz.service.DispatchContext;
49 import org.ofbiz.service.GenericServiceException;
50 import org.ofbiz.service.LocalDispatcher;
51 import org.ofbiz.service.ModelService;
52 import org.ofbiz.service.ServiceUtil;
53
54 /**
55  * Provides generic services related to preparing and
56  * delivering notifications via email.
57  * <p>
58  * To use the NotificationService, a message specific service should be
59  * defined for a particular <a HREF="http://freemarker.sourceforge.net/docs/dgui_quickstart_template.html">
60  * Freemarker Template</a> mapping the required fields of the
61  * template to the required attributes of the service.
62  * <p>
63  * This service definition should extend the <code>sendNotificationInterface<code>
64  * or the <code>prepareNotificationInterface</code> service interface
65  * and simply invoke the associated method defined in this class.
66  * <p>
67  * <blockquote>
68  * <pre>
69  * &lt;service name="sendPoPickupNotification" engine="java"
70  * location="org.ofbiz.content.email.NotificationServices" invoke="sendNotification"&gt;
71  * &lt;description&gt;Sends notification based on a message template&lt;/description&gt;
72  * &lt;implements service="sendNotificationInterface"/&gt;
73  * &lt;attribute name="orderId" type="String" mode="IN" optional="false"/&gt;
74  * &lt;/service&gt;
75  * </pre>
76  * </blockquote>
77  * <p>
78  * An optional parameter available to all message templates is <code>baseUrl</code>
79  * which can either be specified when the service is invoked or let the
80  * <code>NotificationService</code> attempt to resolve it as best it can,
81  * see {@link #setBaseUrl(GenericDelegator, String, Map) setBaseUrl(Map)} for details on how this is achieved.
82  * <p>
83  * The following example shows what a simple notification message template,
84  * associated with the above service, might contain:
85  * <blockquote>
86  * <pre>
87  * Please use the following link to schedule a delivery date:
88  * &lt;p&gt;
89  * ${baseUrl}/ordermgr/control/schedulepo?orderId=${orderId}"
90  * </pre>
91  * </blockquote>
92  * <p>
93  * The template file must be found on the classpath at runtime and
94  * match the "templateName" field passed to the service when it
95  * is invoked.
96  * <p>
97  * For complex messages with a large number of dynamic fields, it may be wise
98  * to implement a custom service that takes one or two parameters that can
99  * be used to resolve the rest of the required fields and then pass them to
100  * the {@link #prepareNotification(DispatchContext, Map) prepareNotification(DispatchContext, Map)}
101  * or {@link #sendNotification(DispatchContext, Map) sendNotification(DispatchContext, Map)}
102  * methods directly to generate or generate and send the notification respectively.
103  *
104  *
105  * @author <a HREF="mailto:tristana@twibble.org">Tristan Austin</a>
106  * @author <a HREF="mailto:jaz@ofbiz.org">Andy Zeneski</a>
107  * @version $Rev: 6642 $
108  * @since 2.2
109  */

110 public class NotificationServices {
111
112     public static final String JavaDoc module = NotificationServices.class.getName();
113
114     /**
115      * This will use the {@link #prepareNotification(DispatchContext, Map) prepareNotification(DispatchContext, Map)}
116      * method to generate the body of the notification message to send
117      * and then deliver it via the "sendMail" service.
118      * <p>
119      * If the "body" parameter is already specified, the message body generation
120      * phase will be skipped and the notification will be sent with the
121      * specified body instead. This can be used to combine both service
122      * calls in a decoupled manner if other steps are required between
123      * generating the message body and sending the notification.
124      *
125      * @param ctx The dispatching context of the service
126      * @param context The map containing all the fields associated with
127      * the sevice
128      * @return A Map with the service response messages in it
129      */

130     public static Map JavaDoc sendNotification(DispatchContext ctx, Map JavaDoc context) {
131         LocalDispatcher dispatcher = ctx.getDispatcher();
132         Map JavaDoc result = null;
133                 
134         try {
135             // see whether the optional 'body' attribute was specified or needs to be processed
136
// nulls are handled the same as not specified
137
String JavaDoc body = (String JavaDoc) context.get("body");
138             
139             if (body == null) {
140                 // prepare the body of the notification email
141
Map JavaDoc bodyResult = prepareNotification(ctx, context);
142                 
143                 // ensure the body was generated successfully
144
if (bodyResult.get(ModelService.RESPONSE_MESSAGE).equals(ModelService.RESPOND_SUCCESS)) {
145                     body = (String JavaDoc) bodyResult.get("body");
146                 } else {
147                     // otherwise just report the error
148
Debug.logError("prepareNotification failed: " + bodyResult.get(ModelService.ERROR_MESSAGE), module);
149                     body = null;
150                 }
151             }
152             
153             // make sure we have a valid body before sending
154
if (body != null) {
155                 // retain only the required attributes for the sendMail service
156
Map JavaDoc emailContext = new HashMap JavaDoc();
157                 emailContext.put("sendTo", context.get("sendTo"));
158                 emailContext.put("body", body);
159                 emailContext.put("sendCc", context.get("sendCc"));
160                 emailContext.put("sendBcc", context.get("sendBcc"));
161                 emailContext.put("sendFrom", context.get("sendFrom"));
162                 emailContext.put("subject", context.get("subject"));
163                 emailContext.put("sendVia", context.get("sendVia"));
164                 emailContext.put("sendType", context.get("sendType"));
165                 emailContext.put("contentType", context.get("contentType"));
166     
167                 // pass on to the sendMail service
168
result = dispatcher.runSync("sendMail", emailContext);
169             } else {
170                 Debug.logError("Invalid email body; null is not allowed", module);
171                 result = ServiceUtil.returnError("Invalid email body; null is not allowed");
172             }
173         } catch (GenericServiceException serviceException) {
174             Debug.logError(serviceException, "Error sending email", module);
175             result = ServiceUtil.returnError("Email delivery error, see error log");
176         }
177
178         return result;
179     }
180
181     /**
182      * This will process the associated notification template definition
183      * with all the fields contained in the given context and generate
184      * the message body of the notification.
185      * <p>
186      * The result returned will contain the appropriate response
187      * messages indicating succes or failure and the OUT parameter,
188      * "body" containing the generated message.
189      *
190      * @param ctx The dispatching context of the service
191      * @param context The map containing all the fields associated with
192      * the sevice
193      * @return A new Map indicating success or error containing the
194      * body generated from the template and the input parameters.
195      */

196     public static Map JavaDoc prepareNotification(DispatchContext ctx, Map JavaDoc context) {
197         GenericDelegator delegator = ctx.getDelegator();
198         String JavaDoc templateName = (String JavaDoc) context.get("templateName");
199         Map JavaDoc templateData = (Map JavaDoc) context.get("templateData");
200         String JavaDoc webSiteId = (String JavaDoc) context.get("webSiteId");
201                         
202         Map JavaDoc result = null;
203         if (templateData == null) {
204             templateData = new HashMap JavaDoc();
205         }
206                
207         try {
208             // ensure the baseURl is defined
209
setBaseUrl(delegator, webSiteId, templateData);
210                                    
211             // initialize the template reader and processor
212
URL JavaDoc templateUrl = UtilURL.fromResource(templateName);
213             
214             if (templateUrl == null) {
215                 Debug.logError("Problem getting the template URL: " + templateName + " not found", module);
216                 return ServiceUtil.returnError("Problem finding template; see logs");
217             }
218                                     
219             InputStreamReader JavaDoc templateReader = new InputStreamReader JavaDoc(templateUrl.openStream());
220                         
221             // process the template with the given data and write
222
// the email body to the String buffer
223
Writer JavaDoc writer = new StringWriter JavaDoc();
224             FreeMarkerWorker.renderTemplate(templateName, templateReader, templateData, writer);
225
226             // extract the newly created body for the notification email
227
String JavaDoc notificationBody = writer.toString();
228             
229             // generate the successfull reponse
230
result = ServiceUtil.returnSuccess("Message body generated successfully");
231             result.put("body", notificationBody);
232         } catch (IOException JavaDoc ie) {
233             Debug.logError(ie, "Problems reading template", module);
234             result = ServiceUtil.returnError("Template reading problem, see error logs");
235         } catch (TemplateException te) {
236             Debug.logError(te, "Problems processing template", module);
237             result = ServiceUtil.returnError("Template processing problem, see error log");
238         }
239
240         return result;
241     }
242
243     /**
244      * The expectation is that a lot of notification messages will include
245      * a link back to one or more pages in the system, which will require knowledge
246      * of the base URL to extrapolate. This method will ensure that the
247      * <code>baseUrl</code> field is set in the given context.
248      * <p>
249      * If it has been specified a default <code>baseUrl</code> will be
250      * set using a best effort approach. If it is specified in the
251      * url.properties configuration files of the system, that will be
252      * used, otherwise it will attempt resolve the fully qualified
253      * local host name.
254      * <p>
255      * <i>Note:</i> I thought it might be useful to have some dynamic way
256      * of extending the default properties provided by the NotificationService,
257      * such as the <code>baseUrl</code>, perhaps using the standard
258      * <code>ResourceBundle</code> java approach so that both classes
259      * and static files may be invoked.
260      *
261      * @param context The context to check and, if necessary, set the
262      * <code>baseUrl</code>.
263      */

264     public static void setBaseUrl(GenericDelegator delegator, String JavaDoc webSiteId, Map JavaDoc context) {
265         // If the baseUrl was not specified we can do a best effort instead
266
if (!context.containsKey("baseUrl")) {
267             StringBuffer JavaDoc httpBase = null;
268             StringBuffer JavaDoc httpsBase = null;
269                     
270             String JavaDoc localServer = null;
271                    
272             String JavaDoc httpsPort = null;
273             String JavaDoc httpsServer = null;
274             String JavaDoc httpPort = null;
275             String JavaDoc httpServer = null;
276             Boolean JavaDoc enableHttps = null;
277
278             try {
279                 // using just the IP address of localhost if we don't have a defined server
280
InetAddress JavaDoc localHost = InetAddress.getLocalHost();
281                 localServer = localHost.getHostAddress();
282             } catch (UnknownHostException JavaDoc hostException) {
283                 Debug.logWarning(hostException, "Could not determine localhost, using '127.0.0.1'", module);
284                 localServer = "127.0.0.1";
285             }
286
287             // load the properties from the website entity
288
GenericValue webSite = null;
289             if (webSiteId != null) {
290                 try {
291                     webSite = delegator.findByPrimaryKeyCache("WebSite", UtilMisc.toMap("webSiteId", webSiteId));
292                     if (webSite != null) {
293                         httpsPort = webSite.getString("httpsPort");
294                         httpsServer = webSite.getString("httpsHost");
295                         httpPort = webSite.getString("httpPort");
296                         httpServer = webSite.getString("httpHost");
297                         enableHttps = webSite.getBoolean("enableHttps");
298                     }
299                 } catch (GenericEntityException e) {
300                     Debug.logWarning(e, "Problems with WebSite entity; using global defaults", module);
301                 }
302             }
303             
304             // fill in any missing properties with fields from the global file
305
if (UtilValidate.isEmpty(httpsPort)) {
306                 httpsPort = UtilProperties.getPropertyValue("url.properties", "port.https", "443");
307             }
308             if (UtilValidate.isEmpty(httpsServer)) {
309                 httpsServer = UtilProperties.getPropertyValue("url.properties", "force.https.host", localServer);
310             }
311             if (UtilValidate.isEmpty(httpPort)) {
312                 httpPort = UtilProperties.getPropertyValue("url.properties", "port.http", "80");
313             }
314             if (UtilValidate.isEmpty(httpServer)) {
315                 httpServer = UtilProperties.getPropertyValue("url.properties", "force.http.host", localServer);
316             }
317             if (UtilValidate.isEmpty(enableHttps)) {
318                 enableHttps = (UtilProperties.propertyValueEqualsIgnoreCase("url.properties", "port.https.enabled", "Y")) ? Boolean.TRUE : Boolean.FALSE;
319             }
320                                 
321             // prepare the (non-secure) URL
322
httpBase = new StringBuffer JavaDoc("http://");
323             httpBase.append(httpServer);
324             if (!"80".equals(httpPort)) {
325                 httpBase.append(":");
326                 httpBase.append(httpPort);
327             }
328
329             // set the base (non-secure) URL for any messages requiring it
330
context.put("baseUrl", httpBase.toString());
331             
332             if (enableHttps.booleanValue()) {
333                 // prepare the (secure) URL
334
httpsBase = new StringBuffer JavaDoc("https://");
335                 httpsBase.append(httpsServer);
336                 if (!"443".equals(httpsPort)) {
337                     httpsBase.append(":");
338                     httpsBase.append(httpsPort);
339                 }
340     
341                 // set the base (secure) URL for any messages requiring it
342
context.put("baseSecureUrl", httpsBase.toString());
343             } else {
344                 context.put("baseSecureUrl", httpBase.toString());
345             }
346         }
347     }
348 }
349
Popular Tags