Java tutorial
/* * 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 static java.lang.Math.max; import static java.lang.Math.min; import com.google.common.base.Joiner; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.io.ByteSource; import org.ambraproject.rhino.content.xml.ArticleXml; import org.ambraproject.rhino.content.xml.XpathReader; import org.ambraproject.rhino.identity.ArticleFileIdentifier; import org.ambraproject.rhino.identity.ArticleIdentifier; import org.ambraproject.rhino.identity.ArticleIngestionIdentifier; import org.ambraproject.rhino.identity.ArticleItemIdentifier; import org.ambraproject.rhino.identity.ArticleRevisionIdentifier; import org.ambraproject.rhino.identity.Doi; import org.ambraproject.rhino.model.Article; import org.ambraproject.rhino.model.ArticleCategoryAssignment; import org.ambraproject.rhino.model.ArticleFile; import org.ambraproject.rhino.model.ArticleIngestion; import org.ambraproject.rhino.model.ArticleItem; import org.ambraproject.rhino.model.ArticleRelationship; import org.ambraproject.rhino.model.ArticleRevision; import org.ambraproject.rhino.model.article.RelatedArticleLink; import org.ambraproject.rhino.rest.RestClientException; import org.ambraproject.rhino.rest.response.CacheableResponse; import org.ambraproject.rhino.rest.response.ServiceResponse; import org.ambraproject.rhino.service.ArticleCrudService; import org.ambraproject.rhino.service.AssetCrudService; import org.ambraproject.rhino.service.taxonomy.TaxonomyService; import org.ambraproject.rhino.util.Archive; import org.ambraproject.rhino.view.ResolvedDoiView; import org.ambraproject.rhino.view.article.ArticleIngestionView; import org.ambraproject.rhino.view.article.ArticleOverview; import org.ambraproject.rhino.view.article.ArticleRevisionView; import org.ambraproject.rhino.view.article.CategoryAssignmentView; import org.ambraproject.rhino.view.article.ItemSetView; import org.ambraproject.rhino.view.article.author.ArticleAllAuthorsView; import org.ambraproject.rhino.view.article.author.AuthorView; import org.apache.commons.lang3.StringEscapeUtils; import org.hibernate.Query; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Order; import org.hibernate.criterion.ProjectionList; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.plos.crepo.model.metadata.RepoObjectMetadata; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.w3c.dom.Document; import javax.xml.xpath.XPathException; import java.io.IOException; import java.io.InputStream; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * Service implementing _c_reate, _r_ead, _u_pdate, and _d_elete operations on article entities and files. */ @SuppressWarnings("JpaQlInspection") public class ArticleCrudServiceImpl extends AmbraService implements ArticleCrudService { private static final Logger LOG = LoggerFactory.getLogger(ArticleCrudServiceImpl.class); private static final Joiner SPACE_JOINER = Joiner.on(' '); public static final int MAX_PAGE_SIZE = 1000; @Autowired AssetCrudService assetCrudService; @Autowired private XpathReader xpathReader; @Autowired private TaxonomyService taxonomyService; @Autowired private ArticleIngestionView.Factory articleIngestionViewFactory; @Autowired private ItemSetView.Factory itemSetViewFactory; @Override public void populateCategories(ArticleIdentifier articleId) throws IOException { Article article = readArticle(articleId); ArticleRevision revision = readLatestRevision(article); taxonomyService.populateCategories(revision); } @Override public Document getManuscriptXml(ArticleIngestion ingestion) { return getManuscriptXml(getManuscriptMetadata(ingestion)); } @Override public Document getManuscriptXml(RepoObjectMetadata objectMetadata) { try (InputStream manuscriptInputStream = contentRepoService.getRepoObject(objectMetadata.getVersion())) { return parseXml(manuscriptInputStream); } catch (IOException e) { throw new RuntimeException(e); } } @Override public RepoObjectMetadata getManuscriptMetadata(ArticleIngestion ingestion) { Doi articleDoi = Doi.create(ingestion.getArticle().getDoi()); ArticleIngestionIdentifier ingestionId = ArticleIngestionIdentifier.create(articleDoi, ingestion.getIngestionNumber()); ArticleItemIdentifier articleItemId = ingestionId.getItemFor(); ArticleFileIdentifier manuscriptId = ArticleFileIdentifier.create(articleItemId, "manuscript"); RepoObjectMetadata objectMetadata = assetCrudService.getArticleItemFile(manuscriptId); return objectMetadata; } @Override public CacheableResponse<ArticleIngestionView> serveMetadata(final ArticleIngestionIdentifier ingestionId) { ArticleIngestion ingestion = readIngestion(ingestionId); return CacheableResponse.serveEntity(ingestion, articleIngestionViewFactory::getView); } @Override public CacheableResponse<ItemSetView> serveItems(ArticleIngestionIdentifier ingestionId) { ArticleIngestion ingestion = readIngestion(ingestionId); return CacheableResponse.serveEntity(ingestion, itemSetViewFactory::getView); } @SuppressWarnings("unchecked") @Override public ArticleOverview buildOverview(Article article) { return hibernateTemplate.execute(session -> { Query ingestionQuery = session.createQuery("FROM ArticleIngestion WHERE article = :article"); ingestionQuery.setParameter("article", article); List<ArticleIngestion> ingestions = ingestionQuery.list(); Query revisionQuery = session.createQuery("" + "FROM ArticleRevision WHERE ingestion IN " + " (FROM ArticleIngestion WHERE article = :article)"); revisionQuery.setParameter("article", article); List<ArticleRevision> revisions = revisionQuery.list(); ArticleIdentifier id = ArticleIdentifier.create(article.getDoi()); return ArticleOverview.build(id, ingestions, revisions); }); } @SuppressWarnings("unchecked") @Override public ServiceResponse<ArticleOverview> serveOverview(ArticleIdentifier id) { ArticleOverview view = hibernateTemplate.execute(session -> { Query ingestionQuery = session.createQuery("FROM ArticleIngestion WHERE article.doi = :doi"); ingestionQuery.setParameter("doi", id.getDoiName()); List<ArticleIngestion> ingestions = ingestionQuery.list(); Query revisionQuery = session.createQuery("" + "FROM ArticleRevision WHERE ingestion IN " + " (FROM ArticleIngestion WHERE article.doi = :doi)"); revisionQuery.setParameter("doi", id.getDoiName()); List<ArticleRevision> revisions = revisionQuery.list(); return ArticleOverview.build(id, ingestions, revisions); }); return ServiceResponse.serveView(view); } @Override public ServiceResponse<List<ArticleRevisionView>> serveRevisions(ArticleIdentifier id) { Article article = readArticle(id); @SuppressWarnings("unchecked") List<ArticleRevision> revisions = (List<ArticleRevision>) hibernateTemplate .find("FROM ArticleRevision WHERE ingestion.article = ? ORDER BY revisionNumber", article); List<ArticleRevisionView> views = Lists.transform(revisions, ArticleRevisionView::getView); return ServiceResponse.serveView(views); } @Override public CacheableResponse<ArticleRevisionView> serveRevision(ArticleRevisionIdentifier revisionId) { ArticleRevision revision = readRevision(revisionId); return CacheableResponse.serveEntity(revision, ArticleRevisionView::getView); } @Override public CacheableResponse<ArticleAllAuthorsView> serveAuthors(ArticleIngestionIdentifier ingestionId) { ArticleIngestion articleIngestion = readIngestion(ingestionId); return CacheableResponse.serveEntity(articleIngestion, ing -> parseAuthors(getManuscriptXml(ing))); } private ArticleAllAuthorsView parseAuthors(Document doc) { List<AuthorView> authors; List<String> authorContributions; List<String> competingInterests; List<String> correspondingAuthorList; try { authors = AuthorsXmlExtractor.getAuthors(doc, xpathReader); authorContributions = AuthorsXmlExtractor.getAuthorContributions(doc, xpathReader); competingInterests = AuthorsXmlExtractor.getCompetingInterests(doc, xpathReader); correspondingAuthorList = AuthorsXmlExtractor.getCorrespondingAuthorList(doc, xpathReader); } catch (XPathException e) { throw new RuntimeException("Invalid XML when parsing authors", e); } return new ArticleAllAuthorsView(authors, authorContributions, competingInterests, correspondingAuthorList); } /** * {@inheritDoc} * * @param articleId */ @Override public ServiceResponse<Collection<CategoryAssignmentView>> serveCategories(final ArticleIdentifier articleId) throws IOException { Article article = readArticle(articleId); Collection<ArticleCategoryAssignment> categoryAssignments = taxonomyService .getAssignmentsForArticle(article); Collection<CategoryAssignmentView> views = categoryAssignments.stream().map(CategoryAssignmentView::new) .collect(Collectors.toList()); return ServiceResponse.serveView(views); } /** * {@inheritDoc} */ @Override public ServiceResponse<List<String>> serveRawCategories(final ArticleIdentifier articleId) throws IOException { Article article = readArticle(articleId); ArticleRevision revision = readLatestRevision(article); Document manuscriptXml = getManuscriptXml(revision.getIngestion()); List<String> rawTerms = taxonomyService.getRawTerms(manuscriptXml, article, false /*isTextRequired*/); List<String> cleanedTerms = new ArrayList<>(); for (String term : rawTerms) { term = term.replaceAll("<TERM>", "").replaceAll("</TERM>", ""); cleanedTerms.add(term); } return ServiceResponse.serveView(cleanedTerms); } /** * {@inheritDoc} * * @param articleId */ @Override public String getRawCategoriesAndText(final ArticleIdentifier articleId) throws IOException { Article article = readArticle(articleId); ArticleRevision revision = readLatestRevision(article); Document manuscriptXml = getManuscriptXml(revision.getIngestion()); List<String> rawTermsAndText = taxonomyService.getRawTerms(manuscriptXml, article, true /*isTextRequired*/); StringBuilder cleanedTermsAndText = new StringBuilder(); cleanedTermsAndText.append("<pre>"); // HTML-escape the text, which is in the first element of the result array cleanedTermsAndText.append(StringEscapeUtils.escapeHtml4(rawTermsAndText.get(0))); cleanedTermsAndText.append("\n"); for (int i = 1; i < rawTermsAndText.size(); i++) { String term = rawTermsAndText.get(i).replaceAll("<TERM>", "").replaceAll("</TERM>", ""); cleanedTermsAndText.append("\n"); cleanedTermsAndText.append(term); } cleanedTermsAndText.append("</pre>"); return cleanedTermsAndText.toString(); } @SuppressWarnings("unchecked") @Override public List<ArticleRelationship> getRelationshipsFrom(ArticleIdentifier sourceId) { return (List<ArticleRelationship>) hibernateTemplate.execute(session -> { Query query = session.createQuery( "" + "SELECT ar " + "FROM ArticleRelationship ar " + "WHERE ar.sourceArticle.doi = :doi "); query.setParameter("doi", sourceId.getDoiName()); return query.list(); }); } @SuppressWarnings("unchecked") @Override public List<ArticleRelationship> getRelationshipsTo(ArticleIdentifier targetId) { return (List<ArticleRelationship>) hibernateTemplate.execute(session -> { Query query = session.createQuery( "" + "SELECT ar " + "FROM ArticleRelationship ar " + "WHERE ar.targetArticle.doi = :doi "); query.setParameter("doi", targetId.getDoiName()); return query.list(); }); } @Override public void refreshArticleRelationships(ArticleRevision sourceArticleRev) { ArticleXml sourceArticleXml = new ArticleXml(getManuscriptXml(sourceArticleRev.getIngestion())); Article sourceArticle = sourceArticleRev.getIngestion().getArticle(); List<RelatedArticleLink> xmlRelationships = sourceArticleXml.parseRelatedArticles(); List<ArticleRelationship> dbRelationships = getRelationshipsFrom( ArticleIdentifier.create(sourceArticle.getDoi())); dbRelationships.forEach(ar -> hibernateTemplate.delete(ar)); xmlRelationships.forEach(ar -> { getArticle(ar.getArticleId()).ifPresent((Article relatedArticle) -> { // if related article exists, persist ArticleRelationship object // otherwise, likely a reference to an article external to our system and so the relationship is not persisted hibernateTemplate.save(fromRelatedArticleLink(sourceArticle, ar)); // refresh related article relationships pointing back to the source article getLatestRevision(relatedArticle).ifPresent((ArticleRevision relatedArticleRev) -> { ArticleXml relatedArticleXml = new ArticleXml( getManuscriptXml(relatedArticleRev.getIngestion())); Set<ArticleRelationship> inboundDbRelationships = getRelationshipsTo( ArticleIdentifier.create(sourceArticle.getDoi())).stream() .filter(dbAr -> dbAr.getSourceArticle().equals(relatedArticle)) .collect(Collectors.toSet()); relatedArticleXml.parseRelatedArticles().stream() .filter(ral -> ral.getArticleId().getDoiName().equals(sourceArticle.getDoi())) .map(ral -> fromRelatedArticleLink(relatedArticle, ral)) .filter(relatedAr -> !inboundDbRelationships.contains(relatedAr)) .forEach(relatedAr -> hibernateTemplate.save(relatedAr)); }); }); }); } private ArticleRelationship fromRelatedArticleLink(Article article, RelatedArticleLink ral) { ArticleRelationship ar = new ArticleRelationship(); ar.setSourceArticle(Objects.requireNonNull(article)); Article targetArticle = getArticle(ral.getArticleId()).orElse(null); ar.setTargetArticle(Objects.requireNonNull(targetArticle)); ar.setType(Objects.requireNonNull(ral.getType())); return ar; } @Override public Archive repack(ArticleIngestionIdentifier ingestionId) { ArticleIngestion ingestion = readIngestion(ingestionId); @SuppressWarnings("unchecked") List<ArticleFile> files = hibernateTemplate.execute(session -> { Query query = session.createQuery("FROM ArticleFile WHERE ingestion = :ingestion"); query.setParameter("ingestion", ingestion); return (List<ArticleFile>) query.list(); }); Map<String, ByteSource> archiveMap = files.stream() .collect(Collectors.toMap(ArticleFile::getIngestedFileName, (ArticleFile file) -> new ByteSource() { @Override public InputStream openStream() throws IOException { return contentRepoService.getRepoObject(file.getCrepoVersion()); } })); return Archive.pack(extractFilenameStub(ingestionId.getDoiName()) + ".zip", archiveMap); } private static final Pattern FILENAME_STUB_PATTERN = Pattern.compile("(?:[^/]*/)*?([^/]*)/?"); private static String extractFilenameStub(String name) { Matcher m = FILENAME_STUB_PATTERN.matcher(name); return m.matches() ? m.group(1) : name; } @Override public ArticleItem getArticleItem(ArticleItemIdentifier id) { return hibernateTemplate.execute(session -> { Query query = session.createQuery("" + "FROM ArticleItem " + "WHERE doi = :doi " + " AND ingestion.ingestionNumber = :ingestionNumber"); query.setParameter("doi", id.getDoiName()); query.setParameter("ingestionNumber", id.getIngestionNumber()); return (ArticleItem) query.uniqueResult(); }); } @SuppressWarnings("unchecked") @Override public Collection<ArticleItem> getAllArticleItems(Doi doi) { return hibernateTemplate.execute(session -> { Query query = session.createQuery("FROM ArticleItem WHERE doi = :doi "); query.setParameter("doi", doi.getName()); return (Collection<ArticleItem>) query.list(); }); } private static boolean isMainArticleItem(ArticleItem item) { return item.getDoi().equals(item.getIngestion().getArticle().getDoi()); } @SuppressWarnings("unchecked") @Override public Collection<ArticleItem> getAllArticleItems(ArticleIngestion ingestion) { return hibernateTemplate.execute(session -> { Query query = session.createQuery("FROM ArticleItem WHERE ingestion = :ingestion"); query.setParameter("ingestion", ingestion); return (Collection<ArticleItem>) query.list(); }); } @SuppressWarnings("unchecked") @Override public Optional<ResolvedDoiView> getItemOverview(Doi doi) { return (Optional<ResolvedDoiView>) hibernateTemplate.execute(session -> { Query ingestionQuery = session.createQuery("FROM ArticleItem WHERE doi = :doi"); ingestionQuery.setParameter("doi", doi.getName()); List<ArticleItem> items = ingestionQuery.list(); if (items.isEmpty()) return Optional.empty(); ResolvedDoiView.DoiWorkType type = items.stream().allMatch(ArticleCrudServiceImpl::isMainArticleItem) ? ResolvedDoiView.DoiWorkType.ARTICLE : ResolvedDoiView.DoiWorkType.ASSET; ArticleIdentifier articleId = Iterables.getOnlyElement( items.stream().map(item -> ArticleIdentifier.create(item.getIngestion().getArticle().getDoi())) .collect(Collectors.toSet())); Query revisionQuery = session.createQuery("" + "FROM ArticleRevision WHERE ingestion IN " + " (SELECT ingestion FROM ArticleItem WHERE doi = :doi)"); revisionQuery.setParameter("doi", doi.getName()); List<ArticleRevision> revisions = revisionQuery.list(); Collection<ArticleIngestion> ingestions = Collections2.transform(items, ArticleItem::getIngestion); ArticleOverview articleOverview = ArticleOverview.build(articleId, ingestions, revisions); return Optional.of(ResolvedDoiView.createForArticle(doi, type, articleOverview)); }); } @Override public Optional<ArticleIngestion> getIngestion(ArticleIngestionIdentifier id) { return Optional.ofNullable(hibernateTemplate.execute(session -> { Query query = session.createQuery("" + "FROM ArticleIngestion " + "WHERE article.doi = :doi " + " AND ingestionNumber = :ingestionNumber"); query.setParameter("doi", id.getDoiName()); query.setParameter("ingestionNumber", id.getIngestionNumber()); return (ArticleIngestion) query.uniqueResult(); })); } @Override public ArticleIngestion readIngestion(ArticleIngestionIdentifier id) { return getIngestion(id) .orElseThrow(() -> new RestClientException("Ingestion not found: " + id, HttpStatus.NOT_FOUND)); } @Override public Optional<ArticleRevision> getRevision(ArticleRevisionIdentifier revisionId) { return Optional.ofNullable(hibernateTemplate.execute(session -> { Query query = session.createQuery("" + "FROM ArticleRevision " + "WHERE revisionNumber = :revisionNumber " + "AND ingestion.article.doi = :doi"); query.setParameter("revisionNumber", revisionId.getRevision()); query.setParameter("doi", revisionId.getDoiName()); return (ArticleRevision) query.uniqueResult(); })); } @Override public ArticleRevision readRevision(ArticleRevisionIdentifier revisionId) { return getRevision(revisionId).orElseThrow( () -> new RestClientException("Revision not found: " + revisionId, HttpStatus.NOT_FOUND)); } @Override public Optional<ArticleRevision> getLatestRevision(Article article) { Integer maxRevisionNumber = hibernateTemplate.execute(session -> { Query query = session.createQuery( "" + "SELECT MAX(rev.revisionNumber) " + "FROM ArticleRevision rev, ArticleItem item " + "WHERE item.doi = :doi " + " AND rev.ingestion = item.ingestion"); query.setParameter("doi", Doi.create(article.getDoi()).getName()); return (Integer) query.uniqueResult(); }); if (maxRevisionNumber == null) return Optional.empty(); return Optional.ofNullable(hibernateTemplate.execute(session -> { Query query = session.createQuery("" + "FROM ArticleRevision as av " + "WHERE av.ingestion.article = :article " + "AND av.revisionNumber = :latestRevision"); query.setParameter("article", article); query.setParameter("latestRevision", maxRevisionNumber); return (ArticleRevision) query.uniqueResult(); })); } @Override public ArticleRevision readLatestRevision(Article article) { return getLatestRevision(article).orElseThrow( () -> new RestClientException("No revisions found for " + article.getDoi(), HttpStatus.NOT_FOUND)); } @Override public Optional<Article> getArticle(ArticleIdentifier articleIdentifier) { return Optional.ofNullable(hibernateTemplate.execute(session -> { Query query = session.createQuery("FROM Article WHERE doi = :doi"); query.setParameter("doi", articleIdentifier.getDoiName()); return (Article) query.uniqueResult(); })); } @Override public Article readArticle(ArticleIdentifier articleIdentifier) { return getArticle(articleIdentifier).orElseThrow( () -> new RestClientException("Article not found: " + articleIdentifier, HttpStatus.NOT_FOUND)); } @Override public Collection<ArticleRevision> getArticlesPublishedOn(LocalDate fromDate, LocalDate toDate) { return getArticlesPublishedOn(fromDate, toDate, null); } @SuppressWarnings("unchecked") @Override public Collection<ArticleRevision> getArticlesPublishedOn(LocalDate fromDate, LocalDate toDate, String bucketName) { return hibernateTemplate.execute(session -> { final String queryString; if (bucketName == null) { queryString = SPACE_JOINER.join("SELECT DISTINCT ar", "FROM ArticleRevision ar", "INNER JOIN ar.ingestion ai", "INNER JOIN ar.ingestion.article at", "WHERE ai.publicationDate >= :fromDate AND ai.publicationDate <= :toDate", "AND ar.revisionId IS NOT NULL"); } else { queryString = SPACE_JOINER.join("SELECT DISTINCT ar", "FROM ArticleRevision ar, ArticleFile file", "INNER JOIN ar.ingestion ai", "INNER JOIN ar.ingestion.article at", "WHERE ai.publicationDate >= :fromDate AND ai.publicationDate <= :toDate", "AND ar.revisionId IS NOT NULL", "AND file.ingestion = ai", "AND file.bucketName = :bucketName"); } Query query = session.createQuery(queryString); query.setParameter("fromDate", java.sql.Date.valueOf(fromDate)); query.setParameter("toDate", java.sql.Date.valueOf(toDate)); if (bucketName != null) { query.setParameter("bucketName", bucketName); } return (Collection<ArticleRevision>) query.list(); }); } @Override public Collection<ArticleRevision> getArticlesRevisedOn(LocalDate fromDate, LocalDate toDate) { return getArticlesRevisedOn(fromDate, toDate, null); } @SuppressWarnings("unchecked") @Override public Collection<ArticleRevision> getArticlesRevisedOn(LocalDate fromDate, LocalDate toDate, String bucketName) { return hibernateTemplate.execute(session -> { final String queryString; if (bucketName == null) { queryString = SPACE_JOINER.join("SELECT DISTINCT ar", "FROM ArticleRevision ar", "INNER JOIN ar.ingestion ai", "INNER JOIN ar.ingestion.article at", "WHERE ai.revisionDate >= :fromDate AND ai.revisionDate <= :toDate", "AND ar.revisionId IS NOT NULL"); } else { queryString = SPACE_JOINER.join("SELECT DISTINCT ar", "FROM ArticleRevision ar, ArticleFile file", "INNER JOIN ar.ingestion ai", "INNER JOIN ar.ingestion.article at", "WHERE ai.revisionDate >= :fromDate AND ai.revisionDate <= :toDate", "AND ar.revisionId IS NOT NULL", "AND file.ingestion = ai", "AND file.bucketName = :bucketName"); } Query query = session.createQuery(queryString); query.setParameter("fromDate", java.sql.Date.valueOf(fromDate)); query.setParameter("toDate", java.sql.Date.valueOf(toDate)); if (bucketName != null) { query.setParameter("bucketName", bucketName); } return (Collection<ArticleRevision>) query.list(); }); } @Override public void updatePreprintDoi(ArticleIngestionIdentifier articleId, String preprintOfDoi) throws IOException { final ArticleIngestion articleIngestion = readIngestion(articleId); articleIngestion.setPreprintDoi(preprintOfDoi); hibernateTemplate.save(articleIngestion); } @Override public Collection<String> getArticleDois(int pageNumber, int pageSize, SortOrder sortOrder) { final Collection<String> articleDois = getArticleDoisForDateRange(pageNumber, pageSize, sortOrder, Optional.empty() /* fromDate */, Optional.empty() /* toDate */); return articleDois; } @Override public Collection<String> getArticleDoisForDateRange(int pageNumber, int pageSize, SortOrder sortOrder, Optional<LocalDateTime> fromDate, Optional<LocalDateTime> toDate) { final long totalArticles = hibernateTemplate.execute(session -> { final Query query = session.createQuery("select count(*) from Article"); final Long count = (Long) query.uniqueResult(); return count; }); if (totalArticles > 0L) { pageNumber = max(pageNumber, 1); final int maxResults = min(pageSize, MAX_PAGE_SIZE); final int firstResult = (pageNumber - 1) * maxResults; if (LOG.isDebugEnabled()) { LOG.debug("pageNumber: {}, pageSize: {}", pageNumber, pageSize); LOG.debug("firstResult: {}, maxResults: {}", firstResult, maxResults); LOG.debug("sortOrder: {}", sortOrder); } if (firstResult < totalArticles) { final DetachedCriteria criteria = DetachedCriteria.forClass(Article.class); final ProjectionList projections = Projections.projectionList(); projections.add(Projections.property("doi" /* propertyName */)); criteria.setProjection(projections); // Set restrictions for filtering on date range, if any. if (fromDate.isPresent()) { criteria.add(Restrictions.ge("created" /* propertyName */, java.sql.Timestamp.valueOf(fromDate.get()))); } if (toDate.isPresent()) { criteria.add(Restrictions.le("created" /* propertyName */, java.sql.Timestamp.valueOf(toDate.get()))); } if (sortOrder == SortOrder.OLDEST) { criteria.addOrder(Order.asc("created" /* propertyName */)); } else { criteria.addOrder(Order.desc("created" /* propertyName */)); } @SuppressWarnings("unchecked") final List<String> articleDois = (List<String>) hibernateTemplate.findByCriteria(criteria, firstResult, maxResults); return articleDois; } } return ImmutableList.of(); } }