View Javadoc

1   /**
2    * This file Copyright (c) 2008-2011 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module.rssaggregator.util;
35  
36  import static java.util.Collections.emptyList;
37  import info.magnolia.cms.core.Content;
38  import info.magnolia.cms.core.search.Query;
39  import info.magnolia.cms.util.QueryUtil;
40  
41  import java.util.ArrayList;
42  import java.util.Collection;
43  import java.util.List;
44  
45  import javax.jcr.Node;
46  import javax.jcr.RepositoryException;
47  
48  import org.slf4j.Logger;
49  import org.slf4j.LoggerFactory;
50  
51  /**
52   * Convenience class to simplify use of Magnolia queries and the mapping of their results to types. <p/> Note that all
53   * queries performed by this class are logged at DEBUG level for convenient debugging when necessary.
54   *
55   * @author Rob van der Linden Vooren
56   * @see ContentMapper
57   */
58  public class MagnoliaTemplate implements MagnoliaQueryOperations {
59  
60      /** Logger for this instance. */
61      protected Logger logger = LoggerFactory.getLogger(getClass());
62  
63      /** {@inheritDoc} */
64      public <T> List<T> queryForList(String repository, String query, String language, String type, ContentMapper<T> mapper) {
65          return query(repository, query, language, type, mapper);
66      }
67  
68      /** {@inheritDoc} */
69      public <T> T queryForObject(String repository, String query, String language, String type, ContentMapper<T> mapper) {
70          List<T> results = query(repository, query, language, type, mapper);
71          return singleResult(results);
72      }
73  
74      /** {@inheritDoc} */
75      public <T> List<T> xpathQueryForList(String repository, String query, String type, ContentMapper<T> mapper) throws DataAccessException {
76          return queryForList(repository, query, Query.XPATH, type, mapper);
77      }
78  
79      /** {@inheritDoc} */
80      public <T> T xpathQueryForObject(String repository, String query, String type, ContentMapper<T> mapper) throws DataAccessException {
81          return queryForObject(repository, query, Query.XPATH, type, mapper);
82      }
83  
84      /** {@inheritDoc} */
85      public Node xpathQueryForContent(String repository, String query, String type) throws DataAccessException {
86          return queryForObject(repository, query, Query.XPATH, type, new ContentMapper<Node>() {
87              public Node map(Node content) throws RepositoryException {
88                  return content;
89              }
90          });
91      }
92  
93      // Helper methods
94  
95      protected <T> List<T> query(String repository, String query, String language, String type, ContentMapper<T> mapper) throws DataAccessException {
96          Collection<Content> contents = queryInternal(repository, query, language, type);
97          if (contents.isEmpty()) {
98              return emptyList();
99          }
100         List<T> result = new ArrayList<T>(contents.size());
101         for (Content content : contents) {
102             try {
103                 result.add(mapper.map(content.getJCRNode()));
104             } catch (RepositoryException re) {
105                 throw new DataAccessException(String.format("Failed to map content '%s' using mapper '%s'", content, mapper.getClass()));
106             }
107         }
108         return result;
109     }
110 
111     /**
112      * Performs the actual query execution. Purposely delegates to {@link QueryUtil#exceptionThrowingQuery(String,
113      * String, String, String) QueryUtil.exceptionThrowingQuery(..)} in order to provide flexibility with regards to
114      * error handling, by translating any {@link RepositoryException} into an unchecked {@link DataAccessException}.
115      * This allows for optional exception handling and logging, making it a better alternative then using {@link
116      * QueryUtil#query(String, String) QueryUtil.query(..)} direcltly, which logs the error and then directly swallows
117      * the {@link RepositoryException exception}. Execute a query for a result list of type T, for the given query.
118      *
119      * @param repository the repository to execute query against
120      * @param query      query to execute
121      * @param language   query language (either {@value Query#XPATH} or {@value Query#SQL}).
122      * @param type       the type of the item to query for
123      * @return the result object of the required type, or {@code null} in case of a {@code null} query
124      * @throws DataAccessException if there is any problem executing the query
125      */
126     protected <T> Collection<T> queryInternal(String repository, String query, String language, String type) {
127         try {
128             return doExceptionThrowingQuery(repository, query, language, type);
129         } catch (RepositoryException re) {
130             throw new DataAccessException("A repository exception occurred", re);
131 
132         }
133     }
134 
135     @SuppressWarnings("unchecked")
136     protected <T> Collection<T> doExceptionThrowingQuery(String repository, String query, String language, String type) throws RepositoryException {
137         logger.debug("Executing {} query for '{}' in repository '{}': \"{}\"", new Object[] {language,
138                 type, repository, query}
139         );
140         Collection<T> results = (Collection<T>) QueryUtil.exceptionThrowingQuery(repository, query, language, type);
141         logger.debug("Query returned %s result(s)", (results == null) ? null : results.size());
142         return results;
143     }
144 
145     /**
146      * Return a single result object from the given collection or {@code null} if the collection is empty or null.
147      *
148      * @param results the result collection (may be {@code null})
149      * @return the single result object, or {@code null} when no result
150      * @throws IncorrectResultSizeDataAccessException
151      *          if collection contains more than one element
152      */
153     protected static <T> T singleResult(Collection<T> results) throws IncorrectResultSizeDataAccessException {
154         int size = (results == null ? 0 : results.size());
155         if (size == 0) {
156             return null;
157         }
158         if (results.size() > 1) {
159             throw new IncorrectResultSizeDataAccessException(1, size);
160         }
161         return results.iterator().next();
162     }
163 
164 }