ResourceDao.java

/*
 * The coLAB project
 * Copyright (C) 2021-2023 AlbaSim, MEI, HEIG-VD, HES-SO
 *
 * Licensed under the MIT License
 */
package ch.colabproject.colab.api.persistence.jpa.document;

import ch.colabproject.colab.api.exceptions.ColabMergeException;
import ch.colabproject.colab.api.model.document.AbstractResource;
import ch.colabproject.colab.api.model.document.Resource;
import ch.colabproject.colab.api.model.document.ResourceRef;
import java.util.List;
import java.util.stream.Collectors;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Resource and resource reference persistence
 * <p>
 * Note : Most of database operations are handled by managed entities and cascade.
 *
 * @author sandra
 */
@Stateless
@LocalBean
public class ResourceDao {

    /** logger */
    private static final Logger logger = LoggerFactory.getLogger(ResourceDao.class);

    /**
     * Access to the persistence unit
     */
    @PersistenceContext(unitName = "COLAB_PU")
    private EntityManager em;

//    /**
//     * Find a resource by id
//     *
//     * @param id the id of the resource to fetch
//     *
//     * @return the resource with the given id or null if such a resource does not exist
//     */
//    private Resource findResource(Long id) {
//        logger.trace("find resource #{}", id);
//
//        return em.find(Resource.class, id);
//    }

//    /**
//     * Find a resource reference by id
//     *
//     * @param id the id of the resource reference to fetch
//     *
//     * @return the resource reference with the given id or null if such a resource reference does
//     *         not exist
//     */
//    private ResourceRef findResourceRef(Long id) {
//        logger.trace("find resource reference #{}", id);
//
//        return em.find(ResourceRef.class, id);
//    }

    /**
     * @param id the id of the resource / resource reference to fetch
     *
     * @return the resource / resource reference with the given id or null if such a resource /
     *         resource reference does not exist
     */
    public AbstractResource findResourceOrRef(Long id) {
        logger.trace("find abstract resource #{}", id);

        return em.find(AbstractResource.class, id);
    }

    /**
     * Retrieve the resources references that have the given target
     *
     * @param target the target
     *
     * @return the matching references
     */
    public List<ResourceRef> findDirectReferences(AbstractResource target) {
        logger.trace("find the direct references of the target {}", target);

        TypedQuery<ResourceRef> query = em.createNamedQuery("ResourceRef.findDirectReferences",
            ResourceRef.class);

        query.setParameter("targetId", target.getId());

        return query.getResultList();
    }

    /**
     * Retrieve all resources which transitively references the given target
     *
     * @param target the target
     *
     * @return the matching references
     */
    public List<ResourceRef> findAllReferences(AbstractResource target) {
        logger.trace("find all references to the target {}", target);

        // first, fetch direct references
        List<ResourceRef> list = findDirectReferences(target);

        // and recurse
        list.addAll(list.stream().flatMap(res -> {
            return findAllReferences(res).stream();
        }).collect(Collectors.toList()));

        return list;
    }

    /**
     * Update resource. Only fields which are editable by users will be impacted.
     *
     * @param <T>           a sub type of AbstractResource
     * @param resourceOrRef the resource as supplied by clients (ie not managed by JPA)
     *
     * @return return updated managed resource
     *
     * @throws ColabMergeException if the update failed
     */
    public <T extends AbstractResource> T updateResourceOrRef(T resourceOrRef)
        throws ColabMergeException {
        logger.trace("update resource or ref {}", resourceOrRef);

        @SuppressWarnings("unchecked")
        T managedResource = (T) this.findResourceOrRef(resourceOrRef.getId());

        managedResource.mergeToUpdate(resourceOrRef);

        return managedResource;
    }

    /**
     * Persist a brand new resource to database
     *
     * @param resource the new resource to persist
     *
     * @return the new persisted and managed resource
     */
    public Resource persistResource(Resource resource) {
        logger.trace("persist resource {}", resource);

        em.persist(resource);

        return resource;
    }

    // Note : there is no persistResourceRef because they are automatically generated
    // and are persisted by cascade

    /**
     * Delete the resource / resource reference from database. This can't be undone
     *
     * @param resourceOrRef the resource / resource reference to delete
     */
    public void deleteResourceOrRef(AbstractResource resourceOrRef) {
        logger.trace("delete abstract resource {}", resourceOrRef);

        // TODO: move to recycle bin first

        em.remove(resourceOrRef);
    }

}