nl.strohalm.cyclos.services.accounts.cards.CardServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.services.accounts.cards.CardServiceImpl.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.accounts.cards;

import java.math.BigInteger;
import java.util.Calendar;
import java.util.List;

import nl.strohalm.cyclos.dao.accounts.cards.CardDAO;
import nl.strohalm.cyclos.dao.accounts.cards.CardLogDAO;
import nl.strohalm.cyclos.entities.Relationship;
import nl.strohalm.cyclos.entities.accounts.cards.Card;
import nl.strohalm.cyclos.entities.accounts.cards.Card.Status;
import nl.strohalm.cyclos.entities.accounts.cards.CardLog;
import nl.strohalm.cyclos.entities.accounts.cards.CardQuery;
import nl.strohalm.cyclos.entities.accounts.cards.CardType;
import nl.strohalm.cyclos.entities.accounts.cards.CardType.CardSecurityCode;
import nl.strohalm.cyclos.entities.groups.BasicGroupSettings.PasswordPolicy;
import nl.strohalm.cyclos.entities.members.Element;
import nl.strohalm.cyclos.entities.members.FullTextMemberQuery;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.exceptions.PermissionDeniedException;
import nl.strohalm.cyclos.services.InitializingService;
import nl.strohalm.cyclos.services.access.AccessServiceLocal;
import nl.strohalm.cyclos.services.elements.BulkMemberActionResultVO;
import nl.strohalm.cyclos.services.elements.ElementServiceLocal;
import nl.strohalm.cyclos.services.fetch.FetchServiceLocal;
import nl.strohalm.cyclos.utils.CacheCleaner;
import nl.strohalm.cyclos.utils.DateHelper;
import nl.strohalm.cyclos.utils.HashHandler;
import nl.strohalm.cyclos.utils.RangeConstraint;
import nl.strohalm.cyclos.utils.RelationshipHelper;
import nl.strohalm.cyclos.utils.access.LoggedUser;
import nl.strohalm.cyclos.utils.validation.LengthValidation;
import nl.strohalm.cyclos.utils.validation.ValidationError;
import nl.strohalm.cyclos.utils.validation.ValidationException;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.math.RandomUtils;

/**
 * Card Service Implementation
 * @author rodrigo
 */
public class CardServiceImpl implements CardServiceLocal, InitializingService {

    private FetchServiceLocal fetchService;
    private CardDAO cardDao;
    private CardLogDAO cardLogDao;
    private ElementServiceLocal elementService;
    private AccessServiceLocal accessService;

    private static final char NUMERIC_CONSTANT = '#';

    private HashHandler hashHandler;

    @Override
    public Card activateCard(Card card, String cardCode) {
        if (card != null && card.getId() > 0) {
            final List<Card> activeCards = cardDao.searchActiveCards(card.getOwner().getId(), card.getId());
            if (activeCards != null) {
                for (final Card activeCard : activeCards) {
                    cancelCard(activeCard);
                }
            }
            card = fetchService.fetch(card, Card.Relationships.CARD_TYPE,
                    RelationshipHelper.nested(Card.Relationships.OWNER, Element.Relationships.USER));
            if (card.getCardType().getCardSecurityCode() == CardType.CardSecurityCode.MANUAL) {
                validateCardSecurityCode(card, cardCode);
                if (!card.getCardType().isShowCardSecurityCode()) {
                    cardCode = hashHandler.hash(card.getOwner().getUser().getSalt(), cardCode);
                }
                card.setCardSecurityCode(cardCode);
            }
            card.setStatus(Card.Status.ACTIVE);
            card.setActivationDate(Calendar.getInstance());
            cardDao.update(card, true);
            generateLog(card);
        }
        return card;
    }

    @Override
    public Card blockCard(final Card card) {
        if (card != null && card.getId() > 0) {
            card.setStatus(Card.Status.BLOCKED);
            cardDao.update(card, true);
            generateLog(card);
        }

        return card;
    }

    @Override
    @SuppressWarnings("unchecked")
    public BulkMemberActionResultVO bulkGenerateNewCard(final FullTextMemberQuery query,
            final boolean generateForPending, final boolean generateForActive) {

        int changed = 0;
        int unchanged = 0;
        boolean generateNewCard = true;
        // force the result type to ITERATOR to avoid load all members in memory
        query.setIterateAll();
        final List<Member> members = (List<Member>) elementService.fullTextSearch(query);
        final CacheCleaner cacheCleaner = new CacheCleaner(fetchService);
        for (Member member : members) {
            member = fetchService.fetch(member, Element.Relationships.GROUP);
            if (member.getMemberGroup().getCardType() != null) {
                final Card card = cardDao.getLastCard(member.getId());
                if (card != null) {
                    if (card.getStatus().equals(Card.Status.PENDING) && !generateForPending) {
                        generateNewCard = false;
                    }
                    if (card.getStatus().equals(Card.Status.ACTIVE) && !generateForActive) {
                        generateNewCard = false;
                    }
                }
            } else {
                generateNewCard = false;
            }

            if (generateNewCard) {
                generateNewCard(member);
                changed++;
            } else {
                unchanged++;
            }

            generateNewCard = true;

            cacheCleaner.clearCache();
        }
        return new BulkMemberActionResultVO(changed, unchanged);
    }

    @Override
    public void cancelAllMemberCards(final Member member) {
        cardDao.cancelAllMemberCards(member);
    }

    @Override
    public Card cancelCard(final Card card) {
        if (card != null && card.getId() > 0) {
            card.setStatus(Card.Status.CANCELED);
            cardDao.update(card, true);
            generateLog(card);
        }
        return card;
    }

    @Override
    public Card changeCardCode(Card card, String code) {
        card = fetchService.fetch(card,
                RelationshipHelper.nested(Card.Relationships.OWNER, Element.Relationships.USER),
                Card.Relationships.CARD_TYPE);
        if (card.getCardType().getCardSecurityCode() != CardSecurityCode.MANUAL) {
            throw new PermissionDeniedException();
        }
        validateCardSecurityCode(card, code);
        if (!card.getCardType().isShowCardSecurityCode()) {
            code = hashHandler.hash(code, card.getOwner().getUser().getSalt());
        }
        card.setCardSecurityCode(code);
        cardDao.update(card);
        return card;
    }

    @Override
    public Card generateNewCard(final Member member) {

        final Card lastCard = cardDao.getLastCard(member.getId());
        if (lastCard != null && lastCard.getStatus() == Card.Status.PENDING) {
            cancelCard(lastCard);
        }
        final Card newCard = buildNewCard(member);
        cardDao.insert(newCard, false);
        generateLog(newCard);
        return newCard;
    }

    @Override
    public Card getActiveCard(final Member member) {
        for (final Card card : getMemberCards(member.getId())) {
            if (card.getStatus() == Status.ACTIVE) {
                return card;
            }
        }
        return null;
    }

    public CardDAO getCardDao() {
        return cardDao;
    }

    public CardLogDAO getCardLogDao() {
        return cardLogDao;
    }

    @Override
    public void initializeService() {
        processCards(Calendar.getInstance());
    }

    @Override
    public Card load(final long cardId, final Relationship... fetch) {
        return cardDao.load(cardId, fetch);
    }

    @Override
    public Card loadByNumber(final BigInteger number, final Relationship... fetch) {
        return cardDao.loadByNumber(number, fetch);
    }

    @Override
    public List<Card> processCards(final Calendar time) {
        return expireCards(time);
    }

    @Override
    public List<Card> search(final CardQuery query) {
        return cardDao.search(query);
    }

    public void setAccessServiceLocal(final AccessServiceLocal accessService) {
        this.accessService = accessService;
    }

    public void setCardDao(final CardDAO cardDao) {
        this.cardDao = cardDao;
    }

    public void setCardLogDao(final CardLogDAO cardLogDao) {
        this.cardLogDao = cardLogDao;
    }

    public void setElementServiceLocal(final ElementServiceLocal elementService) {
        this.elementService = elementService;
    }

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

    public void setHashHandler(final HashHandler hashHandler) {
        this.hashHandler = hashHandler;
    }

    @Override
    public Card unblockCard(final Card card) {
        if (card != null && card.getId() > 0) {
            card.setStatus(Card.Status.ACTIVE);
            cardDao.update(card, true);
            generateLog(card);
        }
        return card;
    }

    @Override
    public void unblockSecurityCode(final Card card) {
        accessService.unblockCardSecurityCode(card.getCardNumber());
    }

    private BigInteger buildCardNumber(final String cardFormatNumber) {

        BigInteger generatedNumber;
        boolean exists = false;
        do {
            final StringBuilder sb = new StringBuilder();
            for (int i = 0; i < cardFormatNumber.length(); i++) {
                final char c = cardFormatNumber.charAt(i);
                if (Character.isDigit(c)) {
                    sb.append(c);
                } else if (c == NUMERIC_CONSTANT) {
                    final int next = i == 0 ? RandomUtils.nextInt(9) + 1 : RandomUtils.nextInt(10); // never generates zero for the first digit
                    sb.append(next);
                }
            }
            generatedNumber = new BigInteger(sb.toString());
            exists = cardDao.existsNumber(generatedNumber);
        } while (exists);

        return generatedNumber;
    }

    private String buildCardSecurityCode(final Integer length) {
        return RandomStringUtils.randomNumeric(length);
    }

    private Card buildNewCard(final Member member) {

        final Calendar now = Calendar.getInstance();

        final Card newCard = new Card();
        final CardType cardType = member.getMemberGroup().getCardType();
        newCard.setCardType(cardType);
        newCard.setOwner(member);
        newCard.setCreationDate(now);
        newCard.setStatus(Status.PENDING);

        final Calendar expirationDate = (Calendar) now.clone();
        expirationDate.add(cardType.getDefaultExpiration().getField().getValue(),
                cardType.getDefaultExpiration().getNumber());
        if (cardType.isIgnoreDayInExpirationDate()) {
            expirationDate.set(Calendar.DAY_OF_MONTH, expirationDate.getActualMaximum(Calendar.DAY_OF_MONTH));
        }
        newCard.setExpirationDate(expirationDate);
        newCard.setCardNumber(buildCardNumber(cardType.getCardFormatNumber()));

        if (cardType.getCardSecurityCode() == CardType.CardSecurityCode.AUTOMATIC) {
            newCard.setCardSecurityCode(buildCardSecurityCode(cardType.getCardSecurityCodeLength().getMax()));
        }

        return newCard;
    }

    /**
     * Verify if has any cards that expires on given time. If has, card status is changed to EXPIRED
     * @param time
     * @return
     */
    private List<Card> expireCards(final Calendar taskTime) {

        final List<Card> cards = cardDao.getCardsToExpire(DateHelper.truncate(taskTime));
        for (final Card card : cards) {
            card.setStatus(Status.EXPIRED);
            cardDao.update(card);
            generateLog(card);
        }
        return cards;
    }

    private void generateLog(final Card card) {
        final CardLog cardLog = new CardLog();
        if (LoggedUser.hasUser()) {
            cardLog.setBy(LoggedUser.element());
        }
        cardLog.setCard(card);
        cardLog.setDate(Calendar.getInstance());
        cardLog.setStatus(card.getStatus());

        cardLogDao.insert(cardLog);

    }

    private List<Card> getMemberCards(final long memberId) {
        return cardDao.getMemberCards(memberId);
    }

    private void validateCardSecurityCode(final Card card, final String code) throws ValidationException {
        final CardType cardType = card.getCardType();
        if (cardType.getCardSecurityCode() != CardSecurityCode.MANUAL) {
            throw new ValidationException();
        }

        final RangeConstraint length = cardType.getCardSecurityCodeLength();
        final ValidationError lengthResult = new LengthValidation(length).validate(card, "securityCode", code);
        if (lengthResult != null) {
            throw new ValidationException("code1", "cardType.cardSecurityCode", lengthResult);
        }

        final PasswordPolicy passwordPolicy = card.getOwner().getGroup().getBasicSettings().getPasswordPolicy();
        final boolean avoidObvious = passwordPolicy != null && passwordPolicy != PasswordPolicy.NONE;
        if (avoidObvious && accessService.isObviousCredential(card.getOwner(), code)) {
            throw new ValidationException("card.changeSecurityCode.error.obvious");
        }
    }

}