org.ambraproject.rhino.service.impl.ArticleListCrudServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ambraproject.rhino.service.impl.ArticleListCrudServiceImpl.java

Source

/*
 * Copyright (c) 2017 Public Library of Science
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

package org.ambraproject.rhino.service.impl;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import org.ambraproject.rhino.identity.ArticleIdentifier;
import org.ambraproject.rhino.identity.ArticleListIdentity;
import org.ambraproject.rhino.model.Article;
import org.ambraproject.rhino.model.ArticleList;
import org.ambraproject.rhino.model.Journal;
import org.ambraproject.rhino.rest.RestClientException;
import org.ambraproject.rhino.rest.response.ServiceResponse;
import org.ambraproject.rhino.service.ArticleListCrudService;
import org.ambraproject.rhino.view.journal.ArticleListView;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.http.HttpStatus;
import org.springframework.orm.hibernate3.HibernateCallback;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

public class ArticleListCrudServiceImpl extends AmbraService implements ArticleListCrudService {

    @Autowired
    private ArticleListView.Factory articleListViewFactory;

    private static Query queryFor(Session hibernateSession, String selectClause, ArticleListIdentity identity) {
        Query query = hibernateSession.createQuery(selectClause + " from Journal j inner join j.articleLists l "
                + "where (j.journalKey=:journalKey) and (l.listKey=:listKey) and (l.listType=:listType)");
        query.setString("journalKey", identity.getJournalKey());
        query.setString("listKey", identity.getKey());
        query.setString("listType", identity.getType());
        return query;
    }

    private boolean listExists(final ArticleListIdentity identity) {
        long count = hibernateTemplate.execute(session -> {
            Query query = queryFor(session, "select count(*)", identity);
            return (Long) query.uniqueResult();
        });
        return count > 0L;
    }

    @Override
    public ArticleListView create(ArticleListIdentity identity, String displayName,
            Set<ArticleIdentifier> articleIds) {
        if (listExists(identity)) {
            throw new RestClientException("List already exists: " + identity, HttpStatus.BAD_REQUEST);
        }

        ArticleList list = new ArticleList();
        list.setListType(identity.getType());
        list.setListKey(identity.getKey());
        list.setDisplayName(displayName);

        list.setArticles(fetchArticles(articleIds));

        Journal journal = (Journal) DataAccessUtils.uniqueResult(hibernateTemplate.findByCriteria(DetachedCriteria
                .forClass(Journal.class).add(Restrictions.eq("journalKey", identity.getJournalKey()))));
        if (journal == null) {
            throw new RestClientException("Journal not found: " + identity.getJournalKey(), HttpStatus.BAD_REQUEST);
        }
        Collection<ArticleList> journalLists = journal.getArticleLists();
        if (journalLists == null) {
            journal.setArticleLists(journalLists = new ArrayList<>(1));
        }
        journalLists.add(list);
        hibernateTemplate.update(journal);

        return articleListViewFactory.getView(list, journal.getJournalKey());
    }

    private ArticleListView getArticleList(final ArticleListIdentity identity) {
        Object[] result = DataAccessUtils
                .uniqueResult(hibernateTemplate.execute((HibernateCallback<List<Object[]>>) session -> {
                    Query query = queryFor(session, "select j.journalKey, l", identity);
                    return query.list();
                }));
        if (result == null) {
            throw nonexistentList(identity);
        }

        String journalKey = (String) result[0];
        ArticleList articleList = (ArticleList) result[1];
        return articleListViewFactory.getView(articleList, journalKey);
    }

    private static RuntimeException nonexistentList(ArticleListIdentity identity) {
        return new RestClientException("List does not exist: " + identity, HttpStatus.NOT_FOUND);
    }

    @Override
    public ArticleListView update(ArticleListIdentity identity, Optional<String> displayName,
            Optional<? extends Set<ArticleIdentifier>> articleIds) {
        ArticleListView listView = getArticleList(identity);
        ArticleList list = listView.getArticleList();

        if (displayName.isPresent()) {
            list.setDisplayName(displayName.get());
        }

        if (articleIds.isPresent()) {
            List<Article> newArticles = fetchArticles(articleIds.get());
            List<Article> oldArticles = list.getArticles();
            oldArticles.clear();
            oldArticles.addAll(newArticles);
        }

        hibernateTemplate.update(list);
        return listView;
    }

    /**
     * Fetch all articles with the given IDs, in the same iteration error.
     *
     * @param articleIds a set of article IDs
     * @return the articles in the same order, if all exist
     * @throws RestClientException if not every article ID belongs to an existing article
     */
    private List<Article> fetchArticles(Set<ArticleIdentifier> articleIds) {
        if (articleIds.isEmpty())
            return ImmutableList.of();
        final Map<String, Integer> articleKeys = new HashMap<>();
        int i = 0;
        for (ArticleIdentifier articleId : articleIds) {
            articleKeys.put(articleId.getDoiName(), i++);
        }

        List<Article> articles = (List<Article>) hibernateTemplate
                .findByNamedParam("from Article where doi in :articleKeys", "articleKeys", articleKeys.keySet());
        if (articles.size() < articleKeys.size()) {
            throw new RestClientException(buildMissingArticleMessage(articles, articleKeys.keySet()),
                    HttpStatus.NOT_FOUND);
        }

        Collections.sort(articles, (a1, a2) -> {
            // We expect the error check above to guarantee that both values will be found in the map
            int i1 = articleKeys.get(a1.getDoi());
            int i2 = articleKeys.get(a2.getDoi());
            return i1 - i2;
        });

        return articles;
    }

    private static String buildMissingArticleMessage(Collection<Article> foundArticles,
            Collection<String> requestedArticleKeys) {
        ImmutableSet.Builder<String> foundArticleKeys = ImmutableSet.builder();
        for (Article foundArticle : foundArticles) {
            foundArticleKeys.add(foundArticle.getDoi());
        }

        Collection<String> missingKeys = Sets.difference(ImmutableSet.copyOf(requestedArticleKeys),
                foundArticleKeys.build());
        missingKeys = new ArrayList<>(missingKeys); // coerce to a type that Gson can handle
        return "Articles not found with DOIs: " + new Gson().toJson(missingKeys);
    }

    @Override
    public ServiceResponse<ArticleListView> read(final ArticleListIdentity identity) {
        return ServiceResponse.serveView(getArticleList(identity));
    }

    private Collection<ArticleListView> asArticleListViews(List<Object[]> results) {
        return asArticleListViews(results, false /*excludeArticleMetadata*/);
    }

    private Collection<ArticleListView> asArticleListViews(List<Object[]> results, boolean excludeArticleMetadata) {
        Collection<ArticleListView> views = new ArrayList<>(results.size());
        for (Object[] result : results) {
            String journalKey = (String) result[0];
            ArticleList articleList = (ArticleList) result[1];
            views.add(articleListViewFactory.getView(articleList, journalKey, excludeArticleMetadata));
        }
        return views;
    }

    @Override
    public ServiceResponse<Collection<ArticleListView>> readAll(final String listType,
            final Optional<String> journalKey) {

        List<Object[]> result = hibernateTemplate.execute((HibernateCallback<List<Object[]>>) session -> {
            StringBuilder queryString = new StringBuilder(125)
                    .append("select j.journalKey, l from Journal j inner join j.articleLists l");

            queryString.append(" where (l.listType=:listType)");
            if (journalKey.isPresent()) {
                queryString.append(" and (j.journalKey=:journalKey)");
            }

            Query query = session.createQuery(queryString.toString());

            query.setParameter("listType", listType);
            if (journalKey.isPresent()) {
                query.setParameter("journalKey", journalKey.get());
            }

            return query.list();
        });
        Collection<ArticleListView> views = asArticleListViews(result);
        return ServiceResponse.serveView(views);
    }

    @Override
    public ServiceResponse<Collection<ArticleListView>> readAll() {

        List<Object[]> result = hibernateTemplate.execute((HibernateCallback<List<Object[]>>) session -> {
            Query query = session.createQuery("select j.journalKey, l from Journal j inner join j.articleLists l");
            return query.list();
        });
        Collection<ArticleListView> views = asArticleListViews(result, true /*excludeArticleMetadata*/);
        return ServiceResponse.serveView(views);
    }

    private Collection<ArticleListView> findContainingLists(final ArticleIdentifier articleId) {
        return hibernateTemplate.execute(session -> {
            Query query = session.createQuery("" + "select j.journalKey, l "
                    + "from Journal j join j.articleLists l join l.articles a " + "where a.doi=:doi");
            query.setString("doi", articleId.getDoiName());
            List<Object[]> results = query.list();
            return asArticleListViews(results);
        });
    }

    @Override
    public ServiceResponse<Collection<ArticleListView>> readContainingLists(final ArticleIdentifier articleId) {
        return ServiceResponse.serveView(findContainingLists(articleId));
    }

}