com.flexive.faces.beans.NewsletterProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.flexive.faces.beans.NewsletterProcessor.java

Source

/***************************************************************
 *  This file is part of the [fleXive](R) framework.
 *
 *  Copyright (c) 1999-2012
 *  UCS - unique computing solutions gmbh (http://www.ucs.at)
 *  All rights reserved
 *
 *  The [fleXive](R) project is free software; you can redistribute
 *  it and/or modify it under the terms of the GNU Lesser General Public
 *  License version 2.1 or higher as published by the Free Software Foundation.
 *
 *  The GNU Lesser General Public License can be found at
 *  http://www.gnu.org/licenses/lgpl.html.
 *  A copy is found in the textfile LGPL.txt and important notices to the
 *  license from the author are found in LICENSE.txt distributed with
 *  these libraries.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  For further information about UCS - unique computing solutions gmbh,
 *  please see the company website: http://www.ucs.at
 *
 *  For further information about [fleXive](R), please see the
 *  project website: http://www.flexive.org
 *
 *
 *  This copyright notice MUST APPEAR in all copies of the file!
 ***************************************************************/
package com.flexive.faces.beans;

import com.flexive.shared.EJBLookup;
import com.flexive.shared.FxContext;
import com.flexive.shared.FxMailUtils;
import com.flexive.shared.content.FxContent;
import com.flexive.shared.content.FxData;
import com.flexive.shared.content.FxPK;
import com.flexive.shared.content.FxPropertyData;
import com.flexive.shared.exceptions.FxApplicationException;
import com.flexive.shared.exceptions.FxNotFoundException;
import com.flexive.shared.interfaces.ContentEngine;
import com.flexive.shared.search.FxResultSet;
import com.flexive.shared.search.query.PropertyValueComparator;
import com.flexive.shared.search.query.SqlQueryBuilder;
import com.flexive.shared.value.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Newsletter business logic
 *
 * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
 * @version $Rev: 1295 $
 */
@SuppressWarnings("UnusedDeclaration")
public class NewsletterProcessor implements Serializable {

    private static final Log LOG = LogFactory.getLog(NewsletterProcessor.class);

    public static final String TYPE_SUBSCRIBER = "NEWSLETTER_SUBSCRIBER";

    public static List<NewsletterInfo> getNewsletters() throws FxApplicationException {
        FxResultSet rs = EJBLookup.getSearchEngine().search(
                "SELECT @pk, #Newsletter/Name, #Newsletter/Description FILTER VERSION=MAX, type='Newsletter'");
        List<NewsletterInfo> result = new ArrayList<NewsletterInfo>(rs.getRowCount());
        for (Object[] col : rs.getRows())
            result.add(new NewsletterInfo((FxPK) col[0], (FxString) col[1], (FxString) col[2]));
        return result;
    }

    /**
     * Return the PK for the newsletter with the given name.
     *
     * @param uniqueName the newsletter name
     * @return the PK for the newsletter with the given name
     * @throws com.flexive.shared.exceptions.FxNotFoundException
     *          if no newsletter with the given name was found
     */
    public static FxPK getNewsletterPK(String uniqueName) throws FxNotFoundException {
        final FxResultSet result;
        try {
            result = new SqlQueryBuilder().select("@pk").type("newsletter")
                    .condition("#newsletter/uniquename", PropertyValueComparator.EQ, uniqueName).getResult();
            if (result.getRowCount() == 0) {
                throw new FxNotFoundException("ex.newsletter.name", uniqueName);
            }
            return result.getResultRow(0).getPk(1);
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        }
    }

    public static synchronized FxPK subscribe(String salutation, String name, String surname, String email,
            boolean sendPlain, boolean sendHTML, FxPK newsletterPK) throws FxApplicationException {
        if (StringUtils.isEmpty(surname))
            throw new FxApplicationException("ex.newsletter.subscriber.missing.surname");
        if (StringUtils.isEmpty(email))
            throw new FxApplicationException("ex.newsletter.subscriber.missing.email");
        email = email.trim().toLowerCase();
        if (!FxMailUtils.isValidEmail(email))
            throw new FxApplicationException("ex.newsletter.email.invalid", email);
        FxContext.get().runAsSystem();
        FxContent newsletter = EJBLookup.getContentEngine().load(newsletterPK);
        try {
            FxResultSet rs = new SqlQueryBuilder().select("@pk")
                    .condition('#' + TYPE_SUBSCRIBER + "/EMAIL", PropertyValueComparator.EQ, email).getResult();
            if (rs.getRowCount() > 0) {
                FxPK foundPK = (FxPK) rs.getObject(1, 1);
                System.out.println("found subscriber with pk " + foundPK);
                FxContent co = EJBLookup.getContentEngine().load(foundPK);
                boolean found = false;
                for (FxValue nl : co.getValues("/NEWSLETTER")) {
                    if (!(nl instanceof FxReference))
                        continue; //should not happen
                    if (((FxReference) nl).getBestTranslation().getId() == newsletterPK.getId()) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    sendActivationMail(((FxString) newsletter.getValue("/SMTPSERVER")).getDefaultTranslation(),
                            ((FxString) newsletter.getValue("/ACTURL")).getDefaultTranslation(), foundPK,
                            newsletterPK, salutation, name, surname, email, sendPlain, sendHTML);
                }
                return foundPK;
            } else {
                FxContent co = EJBLookup.getContentEngine().initialize(TYPE_SUBSCRIBER);
                if (!StringUtils.isEmpty(salutation))
                    co.setValue("/SALUTATION", new FxString(false, salutation));
                if (!StringUtils.isEmpty(name))
                    co.setValue("/NAME", new FxString(false, name));
                co.setValue("/SURNAME", new FxString(true, surname));
                co.setValue("/EMAIL", new FxString(false, email));
                co.setValue("/SENDPLAIN", new FxBoolean(false, sendPlain));
                co.setValue("/SENDHTML", new FxBoolean(false, sendHTML));
                FxPK subsPK = EJBLookup.getContentEngine().save(co);
                sendActivationMail(((FxString) newsletter.getValue("/SMTPSERVER")).getDefaultTranslation(),
                        ((FxString) newsletter.getValue("/ACTURL")).getDefaultTranslation(), subsPK, newsletterPK,
                        salutation, name, surname, email, sendPlain, sendHTML);
                return subsPK;
            }
        } finally {
            FxContext.get().stopRunAsSystem();
        }
    }

    public static List<NewsletterSubscriber> getSubscribers(FxPK newsletterPK) throws FxApplicationException {
        FxResultSet rs = EJBLookup.getSearchEngine().search(
                "SELECT @pk, #Newsletter_Subscriber/Salutation, #Newsletter_Subscriber/Name, #Newsletter_Subscriber/Surname, #Newsletter_Subscriber/EMail, #Newsletter_Subscriber/SENDPLAIN, #Newsletter_Subscriber/SENDHTML, #Newsletter_Subscriber/CODE\n"
                        + "FILTER VERSION=MAX, type='Newsletter_Subscriber', MAX_RESULTROWS=" + Integer.MAX_VALUE
                        + "\n" + "where #Newsletter_Subscriber/NEWSLETTER=" + newsletterPK.getId(),
                0, Integer.MAX_VALUE, null);
        List<NewsletterSubscriber> result = new ArrayList<NewsletterSubscriber>(rs.getRowCount());
        for (Object[] col : rs.getRows())
            result.add(
                    new NewsletterSubscriber((FxPK) col[0], (FxString) col[1], (FxString) col[2], (FxString) col[3],
                            (FxString) col[4], (FxBoolean) col[5], (FxBoolean) col[6], (FxString) col[7]));
        return result;
    }

    public static boolean sendMessage(FxPK messagePK) throws FxApplicationException {
        final ContentEngine ce = EJBLookup.getContentEngine();
        final FxContent message = ce.load(messagePK);
        final FxContent newsletter = ce.load(((FxReference) message.getValue("/NEWSLETTER")).getBestTranslation());
        final List<NewsletterSubscriber> subscribers = getSubscribers(newsletter.getPk());
        if (subscribers.size() == 0) {
            LOG.info("No subscribers for Message " + message.getCaption() + " (pk:" + messagePK + "), Newsletter: "
                    + newsletter.getCaption() + " (pk:" + newsletter.getPk() + ")");
            return false;
        }
        //build the message to send
        String[] attachments = new String[message.getValues("/ATTACHMENT").size()];
        int ai = 0;
        for (FxValue att : message.getValues("/ATTACHMENT")) {
            if (!(att instanceof FxBinary)) {
                LOG.warn("Attachment " + att + " is no FxBinary as expected!");
                continue;
            }
            FxBinary ba = (FxBinary) att;
            File ftmp = null;
            FileOutputStream fout;
            final BinaryDescriptor bdesc = ba.getBestTranslation();
            try {
                ftmp = File.createTempFile("NLAttachment", "bin");
                fout = new FileOutputStream(ftmp);
                bdesc.download(fout);
                fout.close();
                attachments[ai++] = FxMailUtils.encodeAttachment(bdesc.getMimeType(), bdesc.getName(), ftmp);
            } catch (IOException e) {
                LOG.error("Failed to download and encode attachment " + bdesc.getName());
                return false;
            } finally {
                if (ftmp != null)
                    if (!ftmp.delete())
                        ftmp.deleteOnExit();
            }
        }

        final String plainContent = String.valueOf(message.getValue("/CONTENT_PLAIN").getBestTranslation());
        final String htmlContent = String.valueOf(message.getValue("/CONTENT_HTML").getBestTranslation());
        final String subject = String.valueOf(message.getValue("/TITLE").getBestTranslation());
        final String from = String.valueOf(message.getValue("/FROM").getBestTranslation());
        final String replyTo;
        if (message.containsValue("/REPLY_TO") && !message.getValue("/REPLY_TO").isEmpty())
            replyTo = String.valueOf(message.getValue("/REPLY_TO").getBestTranslation());
        else
            replyTo = "";
        FxContent protocol = ce.initialize("NEWSLETTER_PROTOCOL");
        protocol.setValue("/SENT", new FxDateTime(false, new Date(System.currentTimeMillis())));
        protocol.setValue("/NEWSLETTER", new FxReference(false, newsletter.getPk()));
        protocol.setValue("/MESSAGE", new FxReference(false, messagePK));
        final String cancellationURL = ((FxString) newsletter.getValue("/CANCELURL")).getDefaultTranslation();
        final String smtpServer = ((FxString) newsletter.getValue("/SMTPSERVER")).getDefaultTranslation();
        for (NewsletterSubscriber subs : subscribers) {
            boolean sendPlain = !subs.getPlainText().isEmpty() && subs.getPlainText().getBestTranslation();
            boolean sendHTML = !subs.getHtmlText().isEmpty() && subs.getHtmlText().getBestTranslation();
            final String email = subs.getEmail().getBestTranslation();
            if (!sendPlain && !sendHTML) {
                sendPlain = true;
                LOG.warn("Subscriber " + email + " wants neither plain nor html messages! Forcing plain!");
            }
            String plain = sendPlain ? parseContent(newsletter.getPk(), subs, plainContent, cancellationURL) : "";
            String html = sendHTML ? parseContent(newsletter.getPk(), subs, htmlContent, cancellationURL) : "";

            try {
                FxMailUtils.sendMail(smtpServer, subject, plain, html, email, "", "", from, replyTo, attachments);
                protocol.setValue("/SUCCESS", new FxReference(false, subs.getPk()));
                LOG.info("Sent message to " + email);
            } catch (FxApplicationException e) {
                LOG.warn("Failed to send message to " + email);
                protocol.setValue("/FAILURE", new FxReference(false, subs.getPk()));
            }
        }
        ce.save(protocol);
        return true;
    }

    private static String parseContent(FxPK newsletterPK, NewsletterSubscriber subscriber, String content,
            String cancellationURL) {
        final String name = subscriber.getName().isEmpty() ? "" : subscriber.getName().getBestTranslation();
        final boolean hasName = !StringUtils.isEmpty(name);
        final String salutation = subscriber.getSalutation().isEmpty() ? ""
                : subscriber.getSalutation().getBestTranslation();
        final boolean hasSalutation = !StringUtils.isEmpty(salutation);
        if (hasName) {
            content = replace(content, "{{NAME}}", name);
            content = replace(content, "{{HAS_NAME_START}}", "");
            content = replace(content, "{{HAS_NAME_END}}", "");
        } else {
            content = content.replaceAll("\\{\\{HAS_NAME_START\\}\\}.*\\{\\{HAS_NAME_END\\}\\}", "");
            content = replace(content, "{{NAME}}", "");
        }
        content = replace(content, "{{SURNAME}}", subscriber.getSurname().getBestTranslation());
        if (hasSalutation) {
            content = replace(content, "{{SALUTATION}}", salutation);
            content = replace(content, "{{HAS_SALUTATION_START}}", "");
            content = replace(content, "{{HAS_SALUTATION_END}}", "");
        } else {
            content = content.replaceAll("\\{\\{HAS_SALUTATION_START\\}\\}.*\\{\\{HAS_SALUTATION_END\\}\\}", "");
            content = replace(content, "{{SALUTATION}}", "");
        }
        content = replace(content, "{{CANCELLATION_URL}}",
                cancellationURL + subscriber.getCancelCode(newsletterPK));
        return content;
    }

    private static void sendActivationMail(String smtpServer, String activationURL, FxPK subscriberPK,
            FxPK newsletterPK, String salutation, String name, String surname, String email, boolean sendPlain,
            boolean sendHTML) throws FxApplicationException {
        final ContentEngine ce = EJBLookup.getContentEngine();
        final FxContent newsletter = ce.load(newsletterPK);
        final FxContent subscriber = ce.load(subscriberPK);
        final String subject = String.valueOf(newsletter.getValue("/ACTIVATIONSUBJECT").getBestTranslation());
        final String plainContent = String
                .valueOf(newsletter.getValue("/ACTIVATIONCONTENTPLAIN").getBestTranslation());
        final String htmlContent = String
                .valueOf(newsletter.getValue("/ACTIVATIONCONTENTHTML").getBestTranslation());
        final String from = String.valueOf(newsletter.getValue("/ACT_FROM").getBestTranslation());
        final String replyTo;
        if (newsletter.containsValue("/ACT_REPLY_TO") && !newsletter.getValue("/ACT_REPLY_TO").isEmpty())
            replyTo = String.valueOf(newsletter.getValue("/ACT_REPLY_TO").getBestTranslation());
        else
            replyTo = "";
        if (!sendPlain && !sendHTML)
            sendPlain = true;
        String code = String.valueOf(subscriber.getValue("/CODE").getBestTranslation());
        String _activationURL = activationURL + "A" + code + String.valueOf(subscriber.getPk().getId()) + "_"
                + String.valueOf(newsletter.getPk().getId());
        String plain = sendPlain
                ? parseActivationContent(newsletter.getPk(), salutation, name, surname, plainContent,
                        _activationURL)
                : "";
        String html = sendHTML
                ? parseActivationContent(newsletter.getPk(), salutation, name, surname, htmlContent, _activationURL)
                : "";
        FxMailUtils.sendMail(smtpServer, subject, plain, html, email, "", "", from, replyTo);
    }

    private static String parseActivationContent(FxPK newsletterPK, String salutation, String name, String surname,
            String content, String activationURL) {
        final boolean hasName = !StringUtils.isEmpty(name);
        final boolean hasSalutation = !StringUtils.isEmpty(salutation);
        if (hasName) {
            content = replace(content, "{{NAME}}", name);
            content = replace(content, "{{HAS_NAME_START}}", "");
            content = replace(content, "{{HAS_NAME_END}}", "");
        } else {
            content = content.replaceAll("\\{\\{HAS_NAME_START\\}\\}.*\\{\\{HAS_NAME_END\\}\\}", "");
            content = replace(content, "{{NAME}}", "");
        }
        content = replace(content, "{{SURNAME}}", surname);
        if (hasSalutation) {
            content = replace(content, "{{SALUTATION}}", salutation);
            content = replace(content, "{{HAS_SALUTATION_START}}", "");
            content = replace(content, "{{HAS_SALUTATION_END}}", "");
        } else {
            content = content.replaceAll("\\{\\{HAS_SALUTATION_START\\}\\}.*\\{\\{HAS_SALUTATION_END\\}\\}", "");
            content = replace(content, "{{SALUTATION}}", "");
        }
        content = replace(content, "{{ACTIVATION_URL}}", activationURL);
        return content;
    }

    private static void sendWelcomeMail(FxPK subscriberPK, FxPK newsletterPK, NewsletterSubscriber subscriber)
            throws FxApplicationException {
        final ContentEngine ce = EJBLookup.getContentEngine();
        final FxContent newsletter = ce.load(newsletterPK);
        if (newsletter.containsValue("/SENDWELCOME")
                && ((FxBoolean) newsletter.getValue("/SENDWELCOME")).getDefaultTranslation()) {
            LOG.info("Sending welcome email to " + subscriber.getEmail().getBestTranslation());
        } else {
            LOG.info("Not sending welcome email to " + subscriber.getEmail().getBestTranslation());
            return;
        }
        //        final FxContent subscriber = ce.load(subscriberPK);
        final String subject = String.valueOf(newsletter.getValue("/WELCOMESUBJECT").getBestTranslation());
        final String plainContent = String
                .valueOf(newsletter.getValue("/WELCOMECONTENTPLAIN").getBestTranslation());
        final String htmlContent = String.valueOf(newsletter.getValue("/WELCOMECONTENTHTML").getBestTranslation());
        final String from = String.valueOf(newsletter.getValue("/WELCOME_FROM").getBestTranslation());
        final String replyTo;
        if (newsletter.containsValue("/WELCOME_REPLY_TO") && !newsletter.getValue("/WELCOME_REPLY_TO").isEmpty())
            replyTo = String.valueOf(newsletter.getValue("/WELCOME_REPLY_TO").getBestTranslation());
        else
            replyTo = "";
        boolean sendPlain = subscriber.getPlainText().getDefaultTranslation();
        final boolean sendHTML = subscriber.getHtmlText().getDefaultTranslation();
        if (!sendPlain && !sendHTML)
            sendPlain = true;
        final String smtpServer = ((FxString) newsletter.getValue("/SMTPSERVER")).getDefaultTranslation();
        final String cancellationURL = ((FxString) newsletter.getValue("/CANCELURL")).getDefaultTranslation();
        String plain = sendPlain ? parseContent(newsletter.getPk(), subscriber, plainContent, cancellationURL) : "";
        String html = sendHTML ? parseContent(newsletter.getPk(), subscriber, htmlContent, cancellationURL) : "";
        FxMailUtils.sendMail(smtpServer, subject, plain, html, subscriber.getEmail().getDefaultTranslation(), "",
                "", from, replyTo);
    }

    public static boolean processNewsletterRequest(String code) throws FxApplicationException {
        if (StringUtils.isEmpty(code) || code.length() < 24) {
            LOG.warn("Invalid code: " + code);
            return false;
        }
        char action = code.charAt(0);
        String subsCode = code.substring(1, 21);
        FxPK pkSubs = new FxPK(Long.parseLong(code.substring(21, code.indexOf("_"))));
        FxPK pkNL = new FxPK(Long.parseLong(code.substring(code.indexOf("_") + 1)));
        //load subscriber, newsletter and validate
        final ContentEngine ce = EJBLookup.getContentEngine();
        FxContext.get().runAsSystem();
        try {
            final FxContent subscriber = ce.load(pkSubs);
            if (!subsCode.equals(subscriber.getValue("/CODE").getBestTranslation())) {
                LOG.error("Attempted to supply an invalid subscriber code!");
                return false;
            }
            boolean isSubscribed = false;
            for (FxValue nl : subscriber.getValues("/NEWSLETTER")) {
                if (!(nl instanceof FxReference))
                    continue; //should not happen
                if (((FxReference) nl).getBestTranslation().getId() == pkNL.getId()) {
                    isSubscribed = true;
                    break;
                }
            }
            final FxReference nlRef = new FxReference(false, pkNL);
            switch (action) {
            case 'A': //activate
                if (isSubscribed) {
                    LOG.info("Already subscribed " + pkSubs + " to " + pkNL);
                } else {
                    subscriber.setValue("/NEWSLETTER", nlRef);
                    ce.save(subscriber);
                    LOG.info("Subscribed " + pkSubs + " to " + pkNL);
                    sendWelcomeMail(pkSubs, pkNL, new NewsletterSubscriber(subscriber));
                }
                break;
            case 'C': //cancel
                if (!isSubscribed) {
                    LOG.info("Already cancelled " + pkSubs + " from " + pkNL);
                } else {
                    FxPropertyData nlData = null;
                    for (FxData d : subscriber.getData("/NEWSLETTER")) {
                        if (!d.isProperty())
                            continue; //can not happen actually
                        FxPropertyData p = (FxPropertyData) d;
                        if (((FxReference) p.getValue()).getBestTranslation().getId() == pkNL.getId()) {
                            nlData = p;
                            break;
                        }
                    }
                    if (nlData == null) {
                        LOG.error(
                                "Could not find subscription to newsletter " + pkNL + " for subscriber " + pkSubs);
                    } else {
                        subscriber.getRootGroup().removeChild(nlData);
                        ce.save(subscriber);
                        LOG.info("Cancelled " + pkSubs + " from " + pkNL);
                    }
                }
                break;

            }
        } finally {
            FxContext.get().stopRunAsSystem();
        }
        return true;
    }

    /**
     * Replaces all instances of oldString with newString in line ignoring the case.
     *
     * @param line      the String to search to perform replacements on
     * @param oldString the String that should be replaced by newString
     * @param newString the String that will replace all instances of oldString
     * @return a String will all instances of oldString replaced by newString
     */
    public static String replace(String line, String oldString, String newString) {
        if (line == null)
            return "";
        String lcLine = line.toLowerCase();
        String lcOldString = oldString.toLowerCase();
        int i = 0;
        if ((i = lcLine.indexOf(lcOldString, i)) >= 0) {
            char[] line2 = line.toCharArray();
            char[] newString2 = newString.toCharArray();
            int oLength = oldString.length();
            StringBuilder buf = new StringBuilder(line2.length);
            buf.append(line2, 0, i).append(newString2);
            i += oLength;
            int j = i;
            while ((i = lcLine.indexOf(lcOldString, i)) > 0) {
                buf.append(line2, j, i - j).append(newString2);
                i += oLength;
                j = i;
            }
            buf.append(line2, j, line2.length - j);
            return buf.toString();
        }
        return line;
    }
}