nl.strohalm.cyclos.services.sms.SmsMailingServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.services.sms.SmsMailingServiceImpl.java

Source

/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
    
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
Cyclos 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.
    
You should have received a copy of the GNU General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 */
package nl.strohalm.cyclos.services.sms;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import nl.strohalm.cyclos.dao.sms.SmsMailingDAO;
import nl.strohalm.cyclos.entities.accounts.Account;
import nl.strohalm.cyclos.entities.accounts.AccountStatus;
import nl.strohalm.cyclos.entities.accounts.AccountType;
import nl.strohalm.cyclos.entities.customization.fields.CustomField;
import nl.strohalm.cyclos.entities.customization.fields.MemberCustomField;
import nl.strohalm.cyclos.entities.customization.fields.MemberCustomFieldValue;
import nl.strohalm.cyclos.entities.groups.MemberGroup;
import nl.strohalm.cyclos.entities.members.Element;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.entities.settings.LocalSettings;
import nl.strohalm.cyclos.entities.sms.SmsMailing;
import nl.strohalm.cyclos.entities.sms.SmsMailingQuery;
import nl.strohalm.cyclos.scheduling.polling.SmsMailingSendingPollingTask;
import nl.strohalm.cyclos.services.accounts.AccountDTO;
import nl.strohalm.cyclos.services.accounts.AccountServiceLocal;
import nl.strohalm.cyclos.services.application.ApplicationServiceLocal;
import nl.strohalm.cyclos.services.customization.MemberCustomFieldService;
import nl.strohalm.cyclos.services.customization.MemberCustomFieldServiceLocal;
import nl.strohalm.cyclos.services.elements.MemberServiceLocal;
import nl.strohalm.cyclos.services.fetch.FetchServiceLocal;
import nl.strohalm.cyclos.services.settings.SettingsServiceLocal;
import nl.strohalm.cyclos.utils.CustomFieldHelper;
import nl.strohalm.cyclos.utils.MessageResolver;
import nl.strohalm.cyclos.utils.access.LoggedUser;
import nl.strohalm.cyclos.utils.validation.PropertyValidation;
import nl.strohalm.cyclos.utils.validation.ValidationError;
import nl.strohalm.cyclos.utils.validation.ValidationException;
import nl.strohalm.cyclos.utils.validation.Validator;

import org.apache.commons.collections.CollectionUtils;

/**
 * Service implementation for sms mailings
 * 
 * @author luis
 */
public class SmsMailingServiceImpl implements SmsMailingServiceLocal {

    private class VariableEntry {
        String key;
        String value;

        public VariableEntry(final String key, final String value) {
            this.key = key;
            this.value = value;
        }
    }

    private SmsMailingDAO smsMailingDao;
    private SettingsServiceLocal settingsService;
    private MemberServiceLocal memberService;
    private MemberCustomFieldService memberCustomFieldService;
    private ApplicationServiceLocal applicationService;
    private MessageResolver messageResolver;
    private FetchServiceLocal fetchService;
    private AccountServiceLocal accountService;
    private CustomFieldHelper customFieldHelper;

    @Override
    public Map<String, String> getSmsTextVariables(final List<MemberGroup> groups) {
        return getSmsTextVariables(groups, null, true);

    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public Map<String, String> getSmsTextVariables(final List<MemberGroup> groups, final Member member,
            final boolean onlyVariableNames) {
        final LocalSettings localSettings = settingsService.getLocalSettings();
        Map<String, String> variables = new LinkedHashMap<String, String>();

        // system variables
        if (onlyVariableNames) {
            ArrayList<VariableEntry> vars = new ArrayList<VariableEntry>();
            vars.add(new VariableEntry("system_name", messageResolver.message("smsMailing.systemName")));
            sortByValueAndAddToMap(vars, variables);
        } else {
            variables.put("system_name", localSettings.getApplicationName());
        }

        // Add the element variables
        if (onlyVariableNames) {
            ArrayList<VariableEntry> vars = new ArrayList<VariableEntry>();
            vars.add(new VariableEntry("login", messageResolver.message("login.memberUsername")));
            vars.add(new VariableEntry("name", messageResolver.message("member.memberName")));
            vars.add(new VariableEntry("email", messageResolver.message("member.email")));
            sortByValueAndAddToMap(vars, variables);
        } else {
            variables.putAll((Map) member.getVariableValues(localSettings));
        }

        // Add the account variables
        if (onlyVariableNames) {
            ArrayList<VariableEntry> vars = new ArrayList<VariableEntry>();
            vars.add(new VariableEntry("balance", messageResolver.message("account.balance")));
            vars.add(new VariableEntry("available_balance", messageResolver.message("account.availableBalance")));
            vars.add(new VariableEntry("reserved_amount", messageResolver.message("account.reservedAmount")));
            vars.add(new VariableEntry("credit_limit", messageResolver.message("account.creditLimit")));
            vars.add(new VariableEntry("upper_credit_limit", messageResolver.message("account.upperCreditLimit")));
            sortByValueAndAddToMap(vars, variables);
        } else {
            final List<Account> allAccounts = (List<Account>) accountService.getAccounts(member,
                    Account.Relationships.TYPE);
            final Account defaultAccount = accountService.getDefaultAccountFromList(member, allAccounts);
            AccountType defaultAccountType = defaultAccount == null ? null : defaultAccount.getType();

            final AccountStatus status = accountService
                    .getCurrentStatus(new AccountDTO(member, defaultAccountType));
            variables.putAll((Map) status.getVariableValues(localSettings));
        }

        // Add the custom field variables
        if (onlyVariableNames) {
            final List<MemberCustomField> allFields = memberCustomFieldService.list();
            List<MemberCustomField> customFields = customFieldHelper.onlyInAllGroups(allFields, groups);
            ArrayList<VariableEntry> vars = new ArrayList<VariableEntry>();
            for (MemberCustomField mcf : customFields) {
                vars.add(new VariableEntry(mcf.getInternalName(), mcf.getName()));
            }
            sortByValueAndAddToMap(vars, variables);
        } else {
            Collection<MemberCustomFieldValue> values = member.getCustomValues();
            for (MemberCustomFieldValue fv : values) {
                CustomField cf = fv.getField();
                String fName = cf.getInternalName();
                String fValue = fv.getValue();
                variables.put(fName, fValue);
            }
        }

        return variables;
    }

    @Override
    public Map<String, String> getSmsTextVariables(Member member) {
        member = fetchService.fetch(member, Element.Relationships.GROUP);
        return getSmsTextVariables(Collections.singletonList((MemberGroup) member.getGroup()));
    }

    @Override
    public Member nextMemberToSend(final SmsMailing mailing) {
        return smsMailingDao.nextMemberToSend(mailing);
    }

    @Override
    public SmsMailing nextToSend() {
        SmsMailingQuery query = new SmsMailingQuery();
        query.setUniqueResult();
        query.setFinished(false);
        List<SmsMailing> list = search(query);
        return list.isEmpty() ? null : list.get(0);
    }

    @Override
    public void removeMemberFromPending(final SmsMailing smsMailing, final Member member) {
        smsMailingDao.removeMemberFromPending(smsMailing, member);
    }

    @Override
    public List<SmsMailing> search(final SmsMailingQuery query) {
        return smsMailingDao.search(query);
    }

    @Override
    public SmsMailing send(final SmsMailing smsMailing) {
        return doSend(smsMailing);
    }

    public void setAccountServiceLocal(final AccountServiceLocal accountService) {
        this.accountService = accountService;
    }

    public void setApplicationServiceLocal(final ApplicationServiceLocal applicationService) {
        this.applicationService = applicationService;
    }

    public void setCustomFieldHelper(final CustomFieldHelper customFieldHelper) {
        this.customFieldHelper = customFieldHelper;
    }

    public void setFetchServiceLocal(final FetchServiceLocal fetchService) {
        this.fetchService = fetchService;
    }

    public void setMemberCustomFieldServiceLocal(final MemberCustomFieldServiceLocal memberCustomFieldService) {
        this.memberCustomFieldService = memberCustomFieldService;
    }

    public void setMemberServiceLocal(final MemberServiceLocal memberService) {
        this.memberService = memberService;
    }

    public void setMessageResolver(final MessageResolver messageResolver) {
        this.messageResolver = messageResolver;
    }

    public void setSettingsServiceLocal(final SettingsServiceLocal settingsService) {
        this.settingsService = settingsService;
    }

    public void setSmsMailingDao(final SmsMailingDAO smsMailingDao) {
        this.smsMailingDao = smsMailingDao;
    }

    @Override
    public void validate(final SmsMailing smsMailing, final boolean isMemberRequired) throws ValidationException {
        if (isMemberRequired && CollectionUtils.isNotEmpty(smsMailing.getGroups())) {
            throw new ValidationException();
        }
        getValidator(isMemberRequired).validate(smsMailing);

        validateVariables(smsMailing, isMemberRequired);

    }

    private SmsMailing doSend(SmsMailing smsMailing) {
        validate(smsMailing, smsMailing.isSingleMember());
        smsMailing.setBy(LoggedUser.element());
        smsMailing.setDate(Calendar.getInstance());
        smsMailing.setSentSms(0);

        smsMailing = smsMailingDao.insert(smsMailing);

        // Send each SMS
        if (smsMailing.isSingleMember() || !CollectionUtils.isEmpty(smsMailing.getGroups())) {
            final MemberCustomField smsCustomField = settingsService.getSmsCustomField();
            if (smsCustomField == null) {
                throw new IllegalStateException("No custom field was set as SMS field under local settings");
            }
            smsMailingDao.assignUsersToSend(smsMailing, smsCustomField);
        }

        applicationService.awakePollingTaskOnTransactionCommit(SmsMailingSendingPollingTask.class);

        return smsMailing;
    }

    private Validator getValidator(final boolean isMemberRequired) {
        final Validator validator = new Validator("smsMailing");
        validator.property("text").required().maxLength(160);
        if (isMemberRequired) {
            validator.property("member").required().add(new PropertyValidation() {
                private static final long serialVersionUID = -20792899778722444L;

                @Override
                public ValidationError validate(final Object object, final Object property, final Object value) {
                    // Ensure the member has a mobile phone set
                    final Member member = (Member) value;
                    if (member == null) {
                        return null;
                    }

                    final MemberCustomField smsCustomField = settingsService.getSmsCustomField();
                    if (memberService.hasValueForField(member, smsCustomField)) {
                        return null;
                    }

                    return new ValidationError("smsMailing.error.noMobilePhone");
                }
            });
        }

        return validator;
    }

    private void sortByValueAndAddToMap(final ArrayList<VariableEntry> vars, final Map<String, String> variables) {
        Collections.sort(vars, new Comparator<VariableEntry>() {
            @Override
            public int compare(final VariableEntry o1, final VariableEntry o2) {
                return o1.value.compareTo(o2.value);
            }
        });
        for (VariableEntry variableEntry : vars) {
            variables.put(variableEntry.key, variableEntry.value);
        }
    }

    @SuppressWarnings("unchecked")
    private void validateVariables(final SmsMailing smsMailing, final boolean isMemberRequired) {
        // Validate variables
        String text = smsMailing.getText();
        // The only variables that can appear are the allowed:
        Map<String, String> variables = null;
        if (isMemberRequired) {
            variables = getSmsTextVariables(smsMailing.getMember());
        } else {
            variables = getSmsTextVariables(new ArrayList<MemberGroup>(smsMailing.getGroups()));
        }

        Set<String> parsedVariables = new HashSet<String>();
        Pattern pattern = Pattern.compile("#[a-zA-Z_][a-zA-Z\\d_]*#");
        Matcher matcher = pattern.matcher(text);

        while (matcher.find()) {
            parsedVariables.add(matcher.group().replaceAll("#", ""));
        }

        Collection<String> unexpectedVariables = CollectionUtils.subtract(parsedVariables, variables.keySet());
        if (CollectionUtils.isNotEmpty(unexpectedVariables)) {
            throw new ValidationException("smsMailing.error.variableNotFound", unexpectedVariables);
        }
    }
}