org.obiba.mica.network.service.NetworkService.java Source code

Java tutorial

Introduction

Here is the source code for org.obiba.mica.network.service.NetworkService.java

Source

/*
 * Copyright (c) 2018 OBiBa. All rights reserved.
 *
 * This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.obiba.mica.network.service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;

import org.joda.time.DateTime;
import org.obiba.mica.NoSuchEntityException;
import org.obiba.mica.core.domain.LocalizedString;
import org.obiba.mica.core.domain.PublishCascadingScope;
import org.obiba.mica.core.repository.EntityStateRepository;
import org.obiba.mica.core.service.AbstractGitPersistableService;
import org.obiba.mica.dataset.HarmonizationDatasetRepository;
import org.obiba.mica.file.FileStoreService;
import org.obiba.mica.file.FileUtils;
import org.obiba.mica.file.service.FileSystemService;
import org.obiba.mica.micaConfig.event.MicaConfigUpdatedEvent;
import org.obiba.mica.micaConfig.service.MicaConfigService;
import org.obiba.mica.network.NetworkRepository;
import org.obiba.mica.network.NetworkStateRepository;
import org.obiba.mica.network.NoSuchNetworkException;
import org.obiba.mica.network.domain.Network;
import org.obiba.mica.network.domain.NetworkState;
import org.obiba.mica.network.event.IndexNetworksEvent;
import org.obiba.mica.network.event.NetworkDeletedEvent;
import org.obiba.mica.network.event.NetworkPublishedEvent;
import org.obiba.mica.network.event.NetworkUnpublishedEvent;
import org.obiba.mica.network.event.NetworkUpdatedEvent;
import org.obiba.mica.study.ConstraintException;
import org.obiba.mica.study.service.StudyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;

import static java.util.stream.Collectors.toList;

@Service
@Validated
public class NetworkService extends AbstractGitPersistableService<NetworkState, Network> {

    private static final Logger log = LoggerFactory.getLogger(NetworkService.class);

    @Inject
    private NetworkRepository networkRepository;

    @Inject
    private NetworkStateRepository networkStateRepository;

    @Inject
    private FileSystemService fileSystemService;

    @Inject
    private EventBus eventBus;

    @Inject
    private FileStoreService fileStoreService;

    @Inject
    private MicaConfigService micaConfigService;

    @Inject
    private HarmonizationDatasetRepository harmonizationDatasetRepository;

    @Inject
    private StudyService studyService;

    /**
     * Create or update provided {@link Network}.
     *
     * @param network
     */
    public void save(@NotNull Network network) {
        save(network, null);
    }

    @Override
    public void save(@NotNull @Valid Network network, String comment) {
        saveInternal(network, comment, true);
    }

    @SuppressWarnings("OverlyLongMethod")
    private void saveInternal(@NotNull Network network, String comment, boolean cascade) {
        Network saved = network;

        if (network.isNew()) {
            generateId(saved);
        } else {
            saved = networkRepository.findOne(network.getId());

            if (saved != null) {
                BeanUtils.copyProperties(network, saved, "id", "version", "createdBy", "createdDate",
                        "lastModifiedBy", "lastModifiedDate");
            } else {
                saved = network;
            }
        }

        if (saved.getLogo() != null && saved.getLogo().isJustUploaded()) {
            fileStoreService.save(saved.getLogo().getId());
            saved.getLogo().setJustUploaded(false);
        }

        ImmutableSet<String> invalidRoles = ImmutableSet.copyOf(Sets.difference(saved.membershipRoles(),
                Sets.newHashSet(micaConfigService.getConfig().getRoles())));

        for (String r : invalidRoles) {
            saved.removeRole(r);
        }

        NetworkState networkState = findEntityState(network, () -> {
            NetworkState defaultState = new NetworkState();
            defaultState.setName(network.getName());

            return defaultState;
        });

        if (!network.isNew())
            ensureGitRepository(networkState);

        networkState.incrementRevisionsAhead();
        networkStateRepository.save(networkState);

        saved.setLastModifiedDate(DateTime.now());

        if (cascade)
            networkRepository.saveWithReferences(saved);
        else
            networkRepository.save(saved);

        eventBus.post(new NetworkUpdatedEvent(saved));
        gitService.save(saved, comment);
    }

    /**
     * Find a {@link Network} by its ID.
     *
     * @param id
     * @return
     * @throws NoSuchNetworkException
     */
    @NotNull
    public Network findById(@NotNull String id) throws NoSuchNetworkException {
        Network network = networkRepository.findOne(id);

        if (network == null)
            throw NoSuchNetworkException.withId(id);

        return network;
    }

    /**
     * Find all {@link Network}s, optionally related to
     * a {@link org.obiba.mica.study.domain.Study} ID.
     *
     * @param studyId
     * @return
     */
    public List<Network> findAllNetworks(@Nullable String studyId) {
        if (Strings.isNullOrEmpty(studyId))
            return findAllNetworks();
        return networkRepository.findByStudyIds(studyId);
    }

    /**
     * Get all network identifiers.
     *
     * @return
     */
    public List<String> findAllIds() {
        return networkRepository.findAllExistingIds().stream().map(Network::getId).collect(toList());
    }

    /**
     * Get all {@link Network}s.
     *
     * @return
     */
    public List<Network> findAllNetworks() {
        return networkRepository.findAll();
    }

    /**
     * Get {@link Network}s by id.
     *
     * @return
     */
    public List<Network> findAllNetworks(Iterable<String> ids) {
        return Lists.newArrayList(networkRepository.findAll(ids));
    }

    /**
     * Get all published {@link Network}s.
     *
     * @return
     */
    public List<Network> findAllPublishedNetworks() {
        return findPublishedStates().stream()
                .filter(networkState -> gitService.hasGitRepository(networkState)
                        && !Strings.isNullOrEmpty(networkState.getPublishedTag()))
                .map(networkState -> gitService.readFromTag(networkState, networkState.getPublishedTag(),
                        Network.class))
                .map(n -> {
                    n.getModel();
                    return processNetworkForPublishedNumberOfStudies(n);
                }) // make sure dynamic model is initialized
                .collect(toList());
    }

    /**
     * Index all {@link Network}s.
     */
    public void indexAll() {
        eventBus.post(new IndexNetworksEvent());
    }

    @Caching(evict = { @CacheEvict(value = "aggregations-metadata", key = "'network'") })
    public void publish(@NotNull String id, boolean publish) throws NoSuchEntityException {
        publish(id, publish, PublishCascadingScope.NONE);
    }

    /**
     * Set the publication flag on a {@link Network}.
     *
     * @param id
     * @throws NoSuchNetworkException
     */
    @Caching(evict = { @CacheEvict(value = "aggregations-metadata", key = "'network'") })
    public void publish(@NotNull String id, boolean publish, PublishCascadingScope cascadingScope)
            throws NoSuchEntityException {
        Network network = networkRepository.findOne(id);
        if (network == null)
            return;
        if (publish) {
            processNetworkForPublishedNumberOfStudies(network);
            publishState(id);
            eventBus.post(new NetworkPublishedEvent(network, getCurrentUsername(), cascadingScope));
        } else {
            unPublishState(id);
            eventBus.post(new NetworkUnpublishedEvent(network));
        }
    }

    /**
     * Index a specific {@link Network} without updating it.
     *
     * @param id
     * @throws NoSuchNetworkException
     */
    public void index(@NotNull String id) throws NoSuchNetworkException {
        NetworkState networkState = getEntityState(id);
        Network network = findById(id);

        eventBus.post(new NetworkUpdatedEvent(network));

        processNetworkForPublishedNumberOfStudies(network);
        if (networkState.isPublished())
            eventBus.post(new NetworkPublishedEvent(network, getCurrentUsername()));
        else
            eventBus.post(new NetworkUnpublishedEvent(network));
    }

    private Network processNetworkForPublishedNumberOfStudies(Network network) {
        network.setNumberOfStudies(network.getStudyIds().stream().filter(s -> studyService.isPublished(s)).count());
        return network;
    }

    /**
     * Delete a {@link Network}.
     *
     * @param id
     * @throws NoSuchNetworkException
     */
    public void delete(@NotNull String id) throws NoSuchNetworkException {
        Network network = findById(id);
        checkConstraints(network);
        networkRepository.deleteWithReferences(network);

        if (network.getLogo() != null)
            fileStoreService.delete(network.getLogo().getId());

        fileSystemService.delete(FileUtils.getEntityPath(network));
        networkStateRepository.delete(id);
        gitService.deleteGitRepository(network);
        eventBus.post(new NetworkDeletedEvent(network));
    }

    @Override
    protected String generateId(Network network) {
        ensureAcronym(network);
        String nextId = getNextId(network.getAcronym());
        network.setId(nextId);

        return nextId;
    }

    private String getNextId(LocalizedString suggested) {
        if (suggested == null)
            return null;
        String prefix = suggested.asUrlSafeString().toLowerCase();
        if (Strings.isNullOrEmpty(prefix))
            return null;
        String next = prefix;
        try {
            findById(next);
            for (int i = 1; i <= 1000; i++) {
                next = prefix + "-" + i;
                findById(next);
            }
            return null;
        } catch (NoSuchNetworkException e) {
            return next;
        }
    }

    private void checkConstraints(Network network) {
        Map<String, List<String>> conflicts = new HashMap<>();
        List<String> networkIds = networkRepository.findByNetworkIds(network.getId()).stream().map(Network::getId)
                .collect(toList());

        if (!networkIds.isEmpty())
            conflicts.put("network", networkIds);

        if (!conflicts.isEmpty()) {
            throw new ConstraintException(conflicts);
        }
    }

    private void ensureAcronym(@NotNull Network network) {
        if (network.getAcronym() == null || network.getAcronym().isEmpty()) {
            network.setAcronym(network.getName().asAcronym());
        }
    }

    @Override
    protected EntityStateRepository<NetworkState> getEntityStateRepository() {
        return networkStateRepository;
    }

    @Override
    protected Class<Network> getType() {
        return Network.class;
    }

    @Override
    public String getTypeName() {
        return "network";
    }

    @NotNull
    @Override
    public Network findDraft(@NotNull String id) throws NoSuchEntityException {
        return findById(id);
    }

    @Async
    @Subscribe
    public void micaConfigUpdated(MicaConfigUpdatedEvent event) {
        log.info("Mica config updated. Removing deleted roles from networks.");
        if (!event.getRemovedRoles().isEmpty())
            findAllNetworks().forEach(s -> removeRoles(s, event.getRemovedRoles()));
    }

    private void removeRoles(@NotNull Network network, Iterable<String> roles) {
        saveInternal(network, String.format("Removed roles: %s", Joiner.on(", ").join(roles)), false);
        NetworkState state = findStateById(network.getId());

        if (state.isPublished()) {
            processNetworkForPublishedNumberOfStudies(network);
            publishState(network.getId());
            eventBus.post(new NetworkPublishedEvent(network, getCurrentUsername(), PublishCascadingScope.NONE));
        }
    }
}