Package org.jresponder.message

Source Code of org.jresponder.message.MessageRefImpl

/**
* =========================================================================
*     __ ____   ____  __  ____    ___   __  __ ____    ____ ____
*     || || \\ ||    (( \ || \\  // \\  ||\ || || \\  ||    || \\
*     || ||_// ||==   \\  ||_// ((   )) ||\\|| ||  )) ||==  ||_//
*  |__|| || \\ ||___ \_)) ||     \\_//  || \|| ||_//  ||___ || \\
* =========================================================================
*
* Copyright 2012 Brad Peabody
*
* Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
*
* 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.jresponder.message;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.joda.time.format.ISOPeriodFormat;
import org.joda.time.format.PeriodFormatter;
import org.jresponder.domain.Subscriber;
import org.jresponder.domain.Subscription;
import org.jresponder.engine.SendConfig;
import org.jresponder.util.TextRenderUtil;
import org.jresponder.util.TextUtil;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mail.javamail.MimeMessageHelper;

/**
* Default MessageRef implementation - reads messages from a file on disk,
* renders contents using Velocity
*
* @author bradpeabody
*
*/
public class MessageRefImpl implements MessageRef {
 
  /* ====================================================================== */
  /* Logger boiler plate                                                    */
  /* ====================================================================== */
  private static Logger l = null;
  private Logger logger() { if (l == null) l = LoggerFactory.getLogger(this.getClass()); return l; }
  /* ====================================================================== */

  private String name;
  private File file;
  private String fileContents;
  private long fileContentsTimestamp = 0;
  private Document document;
  private Map<String,String> propMap;
 
  /**
   * Default constructor - make sure to call setFile() and then refresh()
   */
  public MessageRefImpl() {
   
  }
 
  /**
   * Constructor which calls setFile() and refresh() for you
   * @param aFile
   * @throws InvalidMessageException
   */
  public MessageRefImpl(File aFile) throws InvalidMessageException {
    setFile(aFile);
    refresh();
  }
 
  @Override
  public String getName() {
    return name;
  }

  public File getFile() {
    return file;
  }

  public void setFile(File file) {
    this.file = file;
    // id is set to file name without .html extension
    name = file.getName().replaceAll("[.]html$", "");
  }
 
  public String getFileContents() {
    return fileContents;
  }

  @Override
  public synchronized void refresh() throws InvalidMessageException {
   
    try {
     
      logger().debug("MessageRef - Starting refresh for: {}", file.getCanonicalPath());
     
      // set timestamp
      fileContentsTimestamp = file.lastModified();
     
      StringBuilder myStringBuilder = new StringBuilder();
      char[] buf = new char[4096];
      BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
      int len;
      while ((len = r.read(buf)) > 0) {
        myStringBuilder.append(buf, 0, len);
      }
      r.close();
     
      fileContents = myStringBuilder.toString();
     
      document = Jsoup.parse(fileContents, "UTF-8");
     
      propMap = new HashMap<String,String>();
     
      Elements myMetaTagElements = document.select("meta");
      if (myMetaTagElements == null || myMetaTagElements.isEmpty()) {
        throw new InvalidMessageException("No meta tags found in file: "+file.getCanonicalPath());
      }
     
      for (Element myPropElement: myMetaTagElements) {
        String myName = myPropElement.attr("name");
        String myValue = myPropElement.attr("content");
        propMap.put(myName, myValue);
      }
     
      // bodies are not read at all until message generation time
     
    } catch (IOException e) {
      throw new InvalidMessageException(e);
    }

    // debug dump
    if (logger().isDebugEnabled()) {
      for (String myKey: propMap.keySet()) {
        logger().debug("  property -- {}: {}", myKey, (propMap.get(myKey)));
      }
    }
  }

  /**
   * Get a property by string name
   */
  @Override
  public String getProp(String aName) {
    return propMap.get(aName);
  }

  /**
   * Type-safe version of getProp
   */
  @Override
  public String getProp(MessageRefProp aName) {
    return getProp(aName.toString());
  }

  /**
   * Get all property names
   */
  @Override
  public List<String> getPropNames() {
    return new ArrayList<String>(propMap.keySet());
  }

  /**
   * Gets the JR_WAIT_AFTER_LAST_MESSAGE property and parses it as an
   * ISO8601 duration and returns the number of milliseconds it represents.
   * Returns null if not set.
   * @throws IllegalArgumentException if the value is in an invalid format
   * @return
   */
  @Override
  public Long getWaitAfterLastMessage() {
   
    // try to parse time from message
    String myWaitAfterLastMessageString =
        this.getProp(MessageRefProp.JR_WAIT_AFTER_LAST_MESSAGE);
    if (myWaitAfterLastMessageString == null) {
      logger().debug("No JR_WAIT_AFTER_LAST_MESSAGE property found on message (message={})", getName());
      return null;
    }
   
    PeriodFormatter myPeriodFormatter = ISOPeriodFormat.standard();
    try {
      long myDuration = myPeriodFormatter.parsePeriod(myWaitAfterLastMessageString).toStandardDuration().getMillis();
      return myDuration;
    }
    catch (IllegalArgumentException e) {
      logger().error("Unable to parse JR_WAIT_AFTER_LAST_MESSAGE value for (message={}), value was: \"{}\"  (this is a problem you need to fix!!! e.g. for one day, use \"P1D\")", new Object[] { getName(), myWaitAfterLastMessageString });
      throw new IllegalArgumentException("Error parsing JR_WAIT_AFTER_LAST_MESSAGE value: " + myWaitAfterLastMessageString, e);
    }
   
  } 

 
  /**
   * Render a message in the context of a particular subscriber
   * and subscription.
   */
  @Override
  public boolean populateMessage(MimeMessage aMimeMessage, SendConfig aSendConfig, Subscriber aSubscriber, Subscription aSubscription) {
   
    try {
     
      // prepare context
      Map<String,Object> myRenderContext = new HashMap<String,Object>();
      myRenderContext.put("subscriber", aSubscriber);
      myRenderContext.put("subscription", aSubscription);
      myRenderContext.put("config", aSendConfig);
      myRenderContext.put("message", this);
     
      // render the whole file
      String myRenderedFileContents = TextRenderUtil.getInstance().render(fileContents, myRenderContext);
     
      // now parse again with Jsoup
      Document myDocument = Jsoup.parse(myRenderedFileContents);
     
      String myHtmlBody = "";
      String myTextBody = "";
     
      // html body
      Elements myBodyElements = myDocument.select("#htmlbody");
      if (!myBodyElements.isEmpty()) {
        myHtmlBody = myBodyElements.html();
      }
     
      // text body
      Elements myJrTextBodyElements = myDocument.select("#textbody");
      if (!myJrTextBodyElements.isEmpty()) {
        myTextBody = TextUtil.getInstance().getWholeText(myJrTextBodyElements.first());
      }
     
      // now build the actual message
      MimeMessage myMimeMessage = aMimeMessage;
      // wrap it in a MimeMessageHelper - since some things are easier with that
      MimeMessageHelper myMimeMessageHelper = new MimeMessageHelper(myMimeMessage);
     
      // set headers
     
      // subject
      myMimeMessageHelper.setSubject
                (
                  TextRenderUtil.getInstance().render
                  (
                    (String)propMap.get(MessageRefProp.JR_SUBJECT.toString()),
                    myRenderContext
                  )
                );

      // TODO: implement DKIM, figure out subetha
     
      String mySenderEmailPattern = aSendConfig.getSenderEmailPattern();
      String mySenderEmail = TextRenderUtil.getInstance().render(mySenderEmailPattern, myRenderContext);
      myMimeMessage.setSender(new InternetAddress(mySenderEmail));
     
      myMimeMessageHelper.setTo(aSubscriber.getEmail());
     
      // from
      myMimeMessageHelper.setFrom
                (
                  TextRenderUtil.getInstance().render
                  (
                    (String)propMap.get(MessageRefProp.JR_FROM_EMAIL.toString()),
                    myRenderContext
                  ),
                  TextRenderUtil.getInstance().render
                  (
                    (String)propMap.get(MessageRefProp.JR_FROM_NAME.toString()),
                    myRenderContext
                  )
                );
   
 
          // see how to set body
     
      // if we have both text and html, then do multipart
      if (myTextBody.trim().length() > 0 && myHtmlBody.trim().length() > 0) {
       
        // create wrapper multipart/alternative part
        MimeMultipart ma = new MimeMultipart("alternative");
        myMimeMessage.setContent(ma);
        // create the plain text
        BodyPart plainText = new MimeBodyPart();
        plainText.setText(myTextBody);
        ma.addBodyPart(plainText);
        // create the html part
        BodyPart html = new MimeBodyPart();
        html.setContent(myHtmlBody, "text/html");
        ma.addBodyPart(html);
      }
     
      // if only HTML, then just use that
      else if (myHtmlBody.trim().length() > 0) {
        myMimeMessageHelper.setText(myHtmlBody, true);
      }
     
      // if only text, then just use that
      else if (myTextBody.trim().length() > 0) {
        myMimeMessageHelper.setText(myTextBody, false);
      }
     
      // if neither text nor HTML, then the message is being skipped,
      // so we just return null
      else {
        return false;
      }
     
      return true;
     
    }
    catch (MessagingException e) {
      throw new RuntimeException(e);
    }
    catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
     
  }

  @Override
  public synchronized boolean conditionalRefresh() throws InvalidMessageException {
   
    if (file.lastModified() != fileContentsTimestamp) {
      refresh();
      return true;
    }
    return false;
   
  }
 
 
}
TOP

Related Classes of org.jresponder.message.MessageRefImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.