001package org.apache.turbine.util.velocity;
002
003
004/*
005 * Licensed to the Apache Software Foundation (ASF) under one
006 * or more contributor license agreements.  See the NOTICE file
007 * distributed with this work for additional information
008 * regarding copyright ownership.  The ASF licenses this file
009 * to you under the Apache License, Version 2.0 (the
010 * "License"); you may not use this file except in compliance
011 * with the License.  You may obtain a copy of the License at
012 *
013 *   http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing,
016 * software distributed under the License is distributed on an
017 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
018 * KIND, either express or implied.  See the License for the
019 * specific language governing permissions and limitations
020 * under the License.
021 */
022
023import org.apache.commons.lang3.StringUtils;
024import org.apache.commons.mail.EmailException;
025import org.apache.commons.mail.SimpleEmail;
026import org.apache.commons.text.WordUtils;
027import org.apache.logging.log4j.LogManager;
028import org.apache.logging.log4j.Logger;
029import org.apache.turbine.Turbine;
030import org.apache.turbine.TurbineConstants;
031import org.apache.turbine.services.TurbineServices;
032import org.apache.turbine.services.velocity.VelocityService;
033import org.apache.velocity.context.Context;
034
035/**
036 * This is a simple class for sending email from within Velocity.
037 * Essentially, the body of the email is processed with a
038 * Velocity Context object.
039 * The beauty of this is that you can send email from within your
040 * Velocity template or from your business logic in your Java code.
041 * The body of the email is just a Velocity template so you can use
042 * all the template functionality of Velocity within your emails!
043 *
044 * <p>Example Usage (This all needs to be on one line in your
045 * template):
046 *
047 * <p>Setup your context:
048 *
049 * <p><code>context.put ("VelocityEmail", new VelocityEmail() );</code>
050 *
051 * <p>Then, in your template:
052 *
053 * <pre>
054 * $VelocityEmail.setTo("Jon Stevens", "jon@latchkey.com")
055 *     .setFrom("Mom", "mom@mom.com").setSubject("Eat dinner")
056 *     .setTemplate("email/momEmail.vm")
057 *     .setContext($context)
058 * </pre>
059 *
060 * The email/momEmail.wm template will then be parsed with the
061 * Context that was defined with setContext().
062 *
063 * <p>If you want to use this class from within your Java code all you
064 * have to do is something like this:
065 *
066 * <pre>
067 * VelocityEmail ve = new VelocityEmail();
068 * ve.setTo("Jon Stevens", "jon@latchkey.com");
069 * ve.setFrom("Mom", "mom@mom.com").setSubject("Eat dinner");
070 * ve.setContext(context);
071 * ve.setTemplate("email/momEmail.vm")
072 * ve.send();
073 * </pre>
074 *
075 * <p>(Note that when used within a Velocity template, the send method
076 * will be called for you when Velocity tries to convert the
077 * VelocityEmail to a string by calling toString()).</p>
078 *
079 * <p>If you need your email to be word-wrapped, you can add the
080 * following call to those above:
081 *
082 * <pre>
083 * ve.setWordWrap (60);
084 * </pre>
085 *
086 * <p>This class is just a wrapper around the SimpleEmail class from
087 * commons-mail using the JavaMail API.
088 * Thus, it depends on having the
089 * mail.server property set in the TurbineResources.properties file.
090 * If you want to use this class outside of Turbine for general
091 * processing that is also possible by making sure to set the path to
092 * the TurbineResources.properties.  See the
093 * TurbineConfig class for more information.</p>
094 *
095 * <p>You can turn on debugging for the JavaMail API by calling
096 * setDebug(true).  The debugging messages will be written to System.out.
097 *
098 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
099 * @author <a href="mailto:gcoladonato@yahoo.com">Greg Coladonato</a>
100 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
101 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
102 * @version $Id$
103 */
104public class VelocityEmail extends SimpleEmail
105{
106    /** Logging */
107    private static final Logger log = LogManager.getLogger(VelocityEmail.class);
108
109    /** The column to word-wrap at.  <code>0</code> indicates no wrap. */
110    private int wordWrap = 0;
111
112    /** Address of outgoing mail server */
113    private String mailServer;
114
115    /** The template to process, relative to Velocity template directory. */
116    private String template = null;
117
118    /** Velocity context */
119    private Context context = null;
120
121    /**
122     * Constructor
123     */
124    public VelocityEmail()
125    {
126        super();
127    }
128
129    /**
130     * Constructor
131     * @param context the velocity context to use
132     */
133    public VelocityEmail(Context context)
134    {
135        this();
136        this.context = context;
137    }
138
139    /**
140     * To: toName, toEmail
141     *
142     * @param toName A String with the TO toName.
143     * @param toEmail A String with the TO toEmail.
144     * @deprecated use addTo(email,name) instead
145     * @throws EmailException email address could not be parsed
146     * @return A VelocityEmail (self).
147     */
148    @Deprecated
149    public VelocityEmail setTo(String toName, String toEmail)
150            throws EmailException
151    {
152        addTo(toEmail,toName);
153        return this;
154    }
155
156    /**
157     * Velocity template to execute. Path is relative to the Velocity
158     * templates directory.
159     *
160     * @param template relative path of the template to parse including the
161     *                 filename.
162     * @return A VelocityEmail (self).
163     */
164    public VelocityEmail setTemplate(String template)
165    {
166        this.template = template;
167        return this;
168    }
169
170    /**
171     * Set the column at which long lines of text should be word-
172     * wrapped. Setting to zero turns off word-wrap (default).
173     *
174     * NOTE: don't use tabs in your email template document,
175     * or your word-wrapping will be off for the lines with tabs
176     * in them.
177     *
178     * @param wordWrap The column at which to wrap long lines.
179     * @return A VelocityEmail (self).
180     */
181    public VelocityEmail setWordWrap(int wordWrap)
182    {
183        this.wordWrap = wordWrap;
184        return this;
185    }
186
187    /**
188     * Set the context object that will be merged with the
189     * template.
190     *
191     * @param context A Velocity context object.
192     * @return A VelocityEmail (self).
193     */
194    public VelocityEmail setContext(Context context)
195    {
196        this.context = context;
197        return this;
198    }
199
200    /**
201     * Get the context object that will be merged with the
202     * template.
203     *
204     * @return A Context (self).
205     */
206    public Context getContext()
207    {
208        return this.context;
209    }
210
211    /**
212     * Sets the address of the outgoing mail server.  This method
213     * should be used when you need to override the value stored in
214     * TR.props.
215     *
216     * @param serverAddress host name of your outgoing mail server
217     */
218    public void setMailServer(String serverAddress)
219    {
220        this.mailServer = serverAddress;
221    }
222
223    /**
224     * Gets the host name of the outgoing mail server.  If the server
225     * name has not been set by calling setMailServer(), the value
226     * from TR.props for mail.server will be returned.  If TR.props
227     * has no value for mail.server, localhost will be returned.
228     *
229     * @return host name of the mail server.
230     */
231    public String getMailServer()
232    {
233        return StringUtils.isNotEmpty(mailServer) ? mailServer
234                : Turbine.getConfiguration().getString(
235                TurbineConstants.MAIL_SERVER_KEY,
236                TurbineConstants.MAIL_SERVER_DEFAULT);
237    }
238
239    /**
240     * This method sends the email.
241     * <p>If the mail server was not set by calling, setMailServer()
242     * the value of mail.server will be used from TR.props.  If that
243     * value was not set, localhost is used.
244     *
245     * @throws EmailException Failure during merging the velocity
246     * template or sending the email.
247     */
248    @Override
249    public String send() throws EmailException
250    {
251        String body = null;
252        try
253        {
254            // Process the template.
255            VelocityService velocityService = (VelocityService)TurbineServices.getInstance().getService(VelocityService.SERVICE_NAME);
256            body = velocityService.handleRequest(context, template);
257        }
258        catch (Exception e)
259        {
260            throw new EmailException(
261                    "Could not render velocity template", e);
262        }
263
264        // If the caller desires word-wrapping, do it here
265        if (wordWrap > 0)
266        {
267            body = WordUtils.wrap(body, wordWrap,
268                    System.getProperty("line.separator"), false);
269        }
270
271        setMsg(body);
272        setHostName(getMailServer());
273        return super.send();
274    }
275
276    /**
277     * The method toString() calls send() for ease of use within a
278     * Velocity template (see example usage above).
279     *
280     * @return An empty string.
281     */
282    @Override
283    public String toString()
284    {
285        try
286        {
287            send();
288        }
289        catch (EmailException e)
290        {
291            log.error("VelocityEmail error", e);
292        }
293        return "";
294    }
295}