org.brekka.pegasus.core.services.impl.InboxServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.brekka.pegasus.core.services.impl.InboxServiceImpl.java

Source

/*
 * Copyright 2013 the original author or authors.
 *
 * 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.brekka.pegasus.core.services.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.UUID;

import org.brekka.commons.persistence.model.ListingCriteria;
import org.brekka.commons.persistence.support.EntityUtils;
import org.brekka.paveway.core.model.UploadedFiles;
import org.brekka.pegasus.core.PegasusErrorCode;
import org.brekka.pegasus.core.PegasusException;
import org.brekka.pegasus.core.dao.DepositDAO;
import org.brekka.pegasus.core.dao.InboxDAO;
import org.brekka.pegasus.core.event.VaultDeleteEvent;
import org.brekka.pegasus.core.model.AccessorContext;
import org.brekka.pegasus.core.model.Actor;
import org.brekka.pegasus.core.model.AllocationDisposition;
import org.brekka.pegasus.core.model.Deposit;
import org.brekka.pegasus.core.model.Dispatch;
import org.brekka.pegasus.core.model.Division;
import org.brekka.pegasus.core.model.EMailAddress;
import org.brekka.pegasus.core.model.Inbox;
import org.brekka.pegasus.core.model.KeySafe;
import org.brekka.pegasus.core.model.Member;
import org.brekka.pegasus.core.model.MemberContext;
import org.brekka.pegasus.core.model.PegasusTokenType;
import org.brekka.pegasus.core.model.Token;
import org.brekka.pegasus.core.model.Vault;
import org.brekka.pegasus.core.services.InboxService;
import org.brekka.pegasus.core.services.MemberService;
import org.brekka.pegasus.core.services.ProfileService;
import org.brekka.xml.pegasus.v2.model.AllocationDocument;
import org.brekka.xml.pegasus.v2.model.AllocationType;
import org.brekka.xml.pegasus.v2.model.BundleType;
import org.brekka.xml.pegasus.v2.model.DetailsType;
import org.brekka.xml.pegasus.v2.model.InboxType;
import org.brekka.xml.pegasus.v2.model.ProfileType;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

/**
 * Inboxes accept deposits.
 *
 * @author Andrew Taylor (andrew@brekka.org)
 */
@Service
public class InboxServiceImpl extends AllocationServiceSupport
        implements InboxService, ApplicationListener<ApplicationEvent> {

    @Autowired
    private InboxDAO inboxDAO;

    @Autowired
    private DepositDAO depositDAO;

    @Autowired
    private MemberService memberService;

    @Autowired
    private ProfileService profileService;

    @Override
    @Transactional()
    public Inbox createInbox(final String name, final String introduction, final String inboxToken,
            final KeySafe<? extends Actor> keySafe) {
        KeySafe<?> nKeySafe = EntityUtils.narrow(keySafe, KeySafe.class);
        Inbox inbox = new Inbox();
        inbox.setId(UUID.randomUUID());
        if (inboxToken != null) {
            Token token = tokenService.createToken(inboxToken, PegasusTokenType.INBOX);
            inbox.setToken(token);
        }
        inbox.setIntroduction(introduction);
        inbox.setKeySafe(nKeySafe);
        inbox.setName(name);
        inbox.setOwner(nKeySafe.getOwner());
        if (nKeySafe instanceof Division) {
            inbox.setDivision((Division<?>) nKeySafe);
        } else if (nKeySafe instanceof Vault) {
            MemberContext memberContext = memberService.getCurrent();
            if (memberContext != null
                    && EntityUtils.identityEquals(memberContext.getMember(), nKeySafe.getOwner())) {
                InboxType newXmlInbox = memberContext.getProfile().addNewInbox();
                newXmlInbox.setUUID(inbox.getId().toString());
                newXmlInbox.setName(name);
                profileService.currentUserProfileUpdated();
            }
        }
        inboxDAO.create(inbox);
        return inbox;
    }

    @Override
    @Transactional()
    public Deposit createDeposit(final Inbox inbox, final AllocationDisposition disposition,
            final DetailsType details, final DateTime expires, final UploadedFiles files) {
        BundleType bundleType = completeFiles(0, files);
        return createDeposit(inbox, disposition, details, expires, null, bundleType);
    }

    @Override
    @Transactional()
    public Deposit createDeposit(final Inbox inbox, final AllocationDisposition disposition,
            final DetailsType details, final DateTime expires, final Dispatch dispatch) {
        BundleType dispatchBundle = copyDispatchBundle(dispatch, null);
        return createDeposit(inbox, disposition, details, expires, dispatch, dispatchBundle);
    }

    @Override
    @Transactional(readOnly = true)
    public Inbox retrieveForToken(final String inboxToken) {
        Token token = this.tokenService.retrieveByPath(inboxToken);
        Inbox inbox = this.inboxDAO.retrieveByToken(token);
        populateNames(Arrays.asList(inbox));
        return inbox;
    }

    @Override
    @Transactional(readOnly = true)
    public Inbox retrieveById(final UUID inboxId) {
        Inbox inbox = this.inboxDAO.retrieveById(inboxId);
        populateNames(Arrays.asList(inbox));
        return inbox;
    }

    @Override
    @Transactional(readOnly = true)
    public List<Inbox> retrieveForKeySafe(final KeySafe<?> keySafe) {
        List<Inbox> inboxList = this.inboxDAO.retrieveForKeySafe(keySafe);
        populateNames(inboxList);
        return inboxList;
    }

    @Override
    @Transactional(readOnly = true)
    public Inbox retrieveForEMailAddress(final EMailAddress eMailAddress) {
        Inbox inbox = this.inboxDAO.retrieveForEMailAddress(eMailAddress);
        return inbox;
    }

    @Override
    @Transactional(readOnly = true)
    public List<Inbox> retrieveForDivision(final Division<?> division) {
        return this.inboxDAO.retrieveForDivision(division);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Deposit> retrieveDepositsByMember(final Member member,
            final AllocationDisposition allocationDisposition, final boolean personalOnly,
            final boolean includeExpired) {
        return this.depositDAO.retrieveDepositsForParticipant(member, allocationDisposition, personalOnly,
                includeExpired);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Deposit> retrieveDepositsByOwner(final Actor owner,
            final AllocationDisposition allocationDisposition, final boolean includePersonal,
            final boolean includeExpired) {
        return this.depositDAO.retrieveDepositsForCollectiveOwner(owner, allocationDisposition, includePersonal,
                includeExpired);
    }

    @Override
    @Transactional(readOnly = true)
    public Deposit retrieveDeposit(final UUID depositId, final boolean populateDispatches) {
        return populateDeposit(depositId, new DepositLookup() {
            @Override
            public Deposit retrieve(final UUID depositId) {
                return InboxServiceImpl.this.depositDAO.retrieveById(depositId);
            }
        }, populateDispatches);
    }

    @Override
    @Transactional(readOnly = true)
    public Deposit retrieveDeposit(final UUID depositId, final Inbox checkInInbox) {
        Deposit deposit = retrieveDeposit(depositId, false);
        if (deposit == null) {
            return null;
        }
        if (EntityUtils.identityEquals(deposit.getInbox(), checkInInbox)) {
            return deposit;
        }
        throw new PegasusException(PegasusErrorCode.PG745, "The deposit '%s' does not belong to inbox '%s'",
                depositId, checkInInbox.getId());
    }

    @Override
    @Transactional(readOnly = true)
    public Deposit retrieveDeposit(final UUID depositId, final Member memberCanAccess) {
        return populateDeposit(depositId, new DepositLookup() {
            @Override
            public Deposit retrieve(final UUID depositId) {
                return InboxServiceImpl.this.depositDAO.retrieveById(depositId, memberCanAccess);
            }
        }, false);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Inbox> retrieveForMember() {
        MemberContext memberContext = memberService.getCurrent();
        List<Inbox> inboxList = this.inboxDAO.retrieveForMember(memberContext.getMember());
        populateNames(inboxList);
        return inboxList;
    }

    @Override
    @Transactional(readOnly = true)
    public List<Deposit> retrieveDeposits(final Inbox inbox, final boolean releaseXml) {
        List<Deposit> depositList = this.depositDAO.retrieveByInbox(inbox);
        if (releaseXml) {
            for (Deposit deposit : depositList) {
                decryptDocument(deposit);
            }
        }
        return depositList;
    }

    @Override
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void deleteDeposit(final UUID depositId) {
        deleteDepositAfter(depositId, DateTime.now());
    }

    @Override
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void deleteDepositAfter(final UUID depositId, final DateTime after) {
        Deposit deposit = this.depositDAO.retrieveById(depositId);
        deposit.setExpires(after.toDate());
        this.depositDAO.update(deposit);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Deposit> retrieveDepositListing(final Inbox inbox, final DateTime from, final DateTime until,
            final boolean showExpired, final ListingCriteria listingCriteria, final boolean dispatchBased) {
        return retrieveDepositListing(inbox, from, until, showExpired, listingCriteria, dispatchBased, null);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Deposit> retrieveDepositListing(final Inbox inbox, final DateTime from, final DateTime until,
            final boolean showExpired, final ListingCriteria listingCriteria, final boolean dispatchBased,
            final List<? extends Actor> sentByActors) {
        List<Deposit> depositList = this.depositDAO.retrieveListing(inbox, defaultMin(from), defaultMax(until),
                showExpired, listingCriteria, dispatchBased, sentByActors);
        if (dispatchBased) {
            List<Dispatch> dispatchList = new ArrayList<>();
            for (Deposit deposit : depositList) {
                Dispatch derivedFrom = deposit.getDerivedFrom();
                dispatchList.add(derivedFrom);
            }
            this.xmlEntityService.releaseAll(dispatchList, AllocationDocument.class);
        } else {
            this.xmlEntityService.releaseAll(depositList, AllocationDocument.class);
        }
        return depositList;
    }

    @Override
    @Transactional(readOnly = true)
    public int retrieveDepositListingRowCount(final Inbox inbox, final DateTime from, final DateTime until,
            final boolean showExpired, final boolean dispatchBased) {
        return this.depositDAO.retrieveListingRowCount(inbox, defaultMin(from), defaultMax(until), showExpired,
                dispatchBased);
    }

    @Override
    public void onApplicationEvent(final ApplicationEvent event) {
        if (event instanceof VaultDeleteEvent) {
            VaultDeleteEvent vaultDeleteEvent = (VaultDeleteEvent) event;
            this.inboxDAO.deleteWithKeySafe(vaultDeleteEvent.getVault());
        }
    }

    protected Deposit createDeposit(Inbox inbox, final AllocationDisposition disposition, final DetailsType details,
            final DateTime expires, final Dispatch dispatch, final BundleType newBundleType) {
        Deposit deposit = new Deposit();
        deposit.setDerivedFrom(dispatch);

        // Bring the inbox under management
        inbox = this.inboxDAO.retrieveById(inbox.getId());
        KeySafe<?> keySafe = inbox.getKeySafe();
        deposit.setExpires(expires == null ? null : expires.toDate());

        AllocationType allocationType = prepareAllocationType(newBundleType, details);

        // Encrypt the document
        prepareDocument(deposit, allocationType, keySafe);

        deposit.setInbox(inbox);
        deposit.setKeySafe(keySafe);
        deposit.setDisposition(disposition);

        Token token = this.tokenService.generateToken(PegasusTokenType.DEPOSIT);
        deposit.setToken(token);

        MemberContext current = this.memberService.getCurrent();
        if (current != null) {
            Actor activeActor = current.getActiveActor();
            deposit.setActor(activeActor);
        }

        createAllocationFiles(deposit);
        this.depositDAO.create(deposit);

        return deposit;
    }

    protected void populateNames(final Collection<Inbox> inboxes) {
        MemberContext memberContext = memberService.getCurrent();
        if (memberContext == null) {
            return;
        }
        ProfileType profile = memberContext.getProfile();
        if (profile != null) {
            for (Inbox inbox : inboxes) {
                if (inbox != null) {
                    for (int i = 0; i < profile.sizeOfInboxArray(); i++) {
                        InboxType inboxXml = profile.getInboxArray(i);
                        if (inboxXml.getUUID().equals(inbox.getId().toString())) {
                            String name = inboxXml.getName();
                            inbox.setName(name);
                            break;
                        }
                    }
                }
            }
        }
    }

    protected Deposit populateDeposit(final UUID depositId, final DepositLookup depositLookup,
            final boolean populateDispatches) {
        AccessorContext current = AccessorContextImpl.getCurrent();
        Deposit deposit = current.retrieve(depositId, Deposit.class);
        if (deposit == null) {
            // Need to extract the metadata
            deposit = depositLookup.retrieve(depositId);
            if (deposit == null) {
                // Does not exist
                return null;
            }
            if (deposit.getDeleted() != null) {
                // Deposit has been deleted, unable to decrypt
                return deposit;
            }
            if (populateDispatches) {
                decryptDocument(deposit.getDerivedFrom());
            } else {
                decryptDocument(deposit);
            }
        } else {
            // Already unlocked, just refresh
            refreshAllocation(deposit);
        }
        return deposit;
    }

    private static DateTime defaultMin(DateTime from) {
        if (from == null) {
            from = new DateTime(0);
        }
        return from;
    }

    private static DateTime defaultMax(DateTime until) {
        if (until == null) {
            until = new DateTime().plusYears(100);
        }
        return until;
    }

    interface DepositLookup {
        Deposit retrieve(UUID depositId);
    }
}