View Javadoc
1   /**
2    * This file Copyright (c) 2003-2018 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.cms.util;
35  
36  import info.magnolia.cms.core.Content;
37  import info.magnolia.cms.core.ItemType;
38  import info.magnolia.context.MgnlContext;
39  import info.magnolia.jcr.util.NodeUtil;
40  
41  import java.util.ArrayList;
42  import java.util.Arrays;
43  import java.util.Calendar;
44  import java.util.Collection;
45  import java.util.Collections;
46  import java.util.HashSet;
47  import java.util.Iterator;
48  import java.util.Set;
49  import java.util.TimeZone;
50  
51  import javax.jcr.LoginException;
52  import javax.jcr.NodeIterator;
53  import javax.jcr.RepositoryException;
54  import javax.jcr.Session;
55  import javax.jcr.query.InvalidQueryException;
56  import javax.jcr.query.Query;
57  import javax.jcr.query.QueryManager;
58  import javax.jcr.query.QueryResult;
59  import javax.jcr.query.qom.QueryObjectModel;
60  
61  import org.apache.commons.lang3.StringUtils;
62  import org.slf4j.Logger;
63  import org.slf4j.LoggerFactory;
64  
65  /**
66   * Util to execute queries as simple as possible.
67   */
68  public class QueryUtil {
69  
70      private static Logger log = LoggerFactory.getLogger(QueryUtil.class);
71  
72      /**
73       * Executes a query.
74       *
75       * @deprecated Since 4.5.4 use search methods.
76       */
77      public static Collection<Content> query(String repository, String statement) {
78          return query(repository, statement, "sql");
79      }
80  
81      /**
82       * Executes a query.
83       *
84       * @deprecated Since 4.5.4 use search methods.
85       */
86      public static Collection<Content> query(String repository, String statement, String language) {
87          return query(repository, statement, language, ItemType.NT_BASE);
88      }
89  
90      /**
91       * @deprecated Since 4.5.4 use search methods.
92       */
93      public static Collection<Content> exceptionThrowingQuery(String repository, String statement, String language, String returnItemType) throws RepositoryException {
94          return exceptionThrowingQuery(repository, statement, language, returnItemType, Long.MAX_VALUE);
95      }
96  
97      /**
98       * Executes a query, throwing any exceptions that arise as a result.
99       *
100      * @deprecated Since 4.5.4 use search methods.
101      */
102     public static Collection<Content> exceptionThrowingQuery(String repository, String statement, String language, String returnItemType,
103                                                              long maxResultSize) throws RepositoryException {
104         Collection<Content> results = new ArrayList<Content>();
105         if (maxResultSize <= 0) {
106             maxResultSize = Long.MAX_VALUE;
107         }
108         NodeIterator iterator = search(repository, statement, language, returnItemType);
109 
110         long count = 1;
111         while (iterator.hasNext() && count <= maxResultSize) {
112             results.add(ContentUtil.getContent(repository, iterator.nextNode().getPath()));
113             count++;
114         }
115         return results;
116     }
117 
118     /**
119      * @deprecated Since 4.5.4 use search methods.
120      */
121     public static Collection<Content> query(String repository, String statement, String language, String returnItemType) {
122         return query(repository, statement, language, returnItemType, Long.MAX_VALUE);
123     }
124 
125     /**
126      * Executes a query - if an exception is thrown, it is logged and an empty collection is
127      * returned.
128      *
129      * @deprecated Since 4.5.4 use search methods.
130      */
131     @SuppressWarnings("unchecked")
132     // Collections.EMPTY_LIST;
133     public static Collection<Content> query(String repository, String statement, String language, String returnItemType, long maxResultSize) {
134         try {
135             return exceptionThrowingQuery(repository, statement, language, returnItemType, maxResultSize);
136         } catch (Exception e) {
137             log.error("can't execute query [{}], will return empty collection", statement, e);
138         }
139         return Collections.EMPTY_LIST;
140     }
141 
142     /**
143      * @param month 1-12 (as opposed to java.util.Calendar 0-11 notation)
144      * @deprecated
145      */
146     public static String createDateExpression(int year, int month, int day) {
147         Calendar cal = Calendar.getInstance();
148         cal.set(year, month - 1, day);
149         return createDateExpression(cal);
150     }
151 
152     /**
153      * Expression representing a date.
154      *
155      * @deprecated since 4.5.4 use info.magnolia.cms.util.DateUtil.createDateExpression(calendar)
156      */
157     public static String createDateExpression(Calendar calendar) {
158         return DateUtil.createDateExpression(calendar);
159     }
160 
161     /**
162      * @param month 1-12 (as opposed to java.util.Calendar 0-11 notation)
163      * @deprecated
164      */
165     public static String createDateTimeExpression(int year, int month, int day, int hour, int minutes, int seconds) {
166         Calendar cal = Calendar.getInstance();
167         cal.set(year, month - 1, day, hour, minutes, seconds);
168         return createDateTimeExpression(cal);
169     }
170 
171     /**
172      * Expression representing a date and time.
173      *
174      * @deprecated since 4.5.4 use info.magnolia.cms.util.DateUtil.createDateTimeExpression(calendar)
175      */
176     public static String createDateTimeExpression(Calendar calendar) {
177         return DateUtil.createDateTimeExpression(calendar);
178     }
179 
180     /**
181      * @param month 1-12 (as opposed to java.util.Calendar 0-11 notation)
182      * @deprecated
183      */
184     public static String createDateTimeExpressionIgnoreTimeZone(int year, int month, int day, int hour, int minutes, int seconds) {
185         Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
186         cal.set(year, month - 1, day, hour, minutes, seconds);
187         return createDateTimeExpression(cal);
188     }
189 
190     /**
191      * Do not consider the timezone.
192      *
193      * @deprecated since 4.5.4 use info.magnolia.cms.util.DateUtil.createDateTimeExpressionIgnoreTimeZone(calendar)
194      */
195     public static String createDateTimeExpressionIgnoreTimeZone(Calendar calendar) {
196         return DateUtil.createDateTimeExpressionIgnoreTimeZone(calendar);
197     }
198 
199     /**
200      * Executes the query based on QOM and then pops-up in the node hierarchy until returnItemType is found. If the result
201      * is not returnItemType or none of its parents are then next node in result is checked.
202      * Duplicate nodes are removed from result.
203      * For date/time expressions use <code>DateUtil.create*Expression()</code> methods.
204      *
205      * @return Result as NodeIterator
206      */
207     public static NodeIterator search(QueryObjectModel model, String returnItemType) throws InvalidQueryException, RepositoryException {
208         return NodeUtil.filterDuplicates(NodeUtil.filterParentNodeType(model.execute().getNodes(), returnItemType));
209     }
210 
211     /**
212      * Executes the query with given language.Unlike in the old API item type has to be specified in query itself.
213      * <code>SELECT * FROM [mgnl:page]</code> example for selecting just pages in JCR SQL2 language.
214      * Duplicate nodes are removed from result.
215      * For date/time expressions use <code>DateUtil.create*Expression()</code> methods.
216      *
217      * @return Result as NodeIterator
218      */
219     public static NodeIterator search(String workspace, String statement, String language) throws InvalidQueryException, RepositoryException {
220         Session session = MgnlContext.getJCRSession(workspace);
221         QueryManager manager = session.getWorkspace().getQueryManager();
222         Query query = manager.createQuery(statement, language);
223 
224         return NodeUtil.filterDuplicates(query.execute().getNodes());
225     }
226 
227     /**
228      * Executes the query using JCR SQL2 language. Unlike in the old API item type has to be specified in query itself.
229      * <code>SELECT * FROM [mgnl:page]</code> example for selecting just pages.
230      * For executing old query use info.magnolia.cms.util.QueryUtil.search(String workspace, String statement, String language)
231      * where you specify <code>Query.SQL</code> as the language.
232      * For date/time expressions use <code>DateUtil.create*Expression()</code> methods.
233      *
234      * @return Result as NodeIterator
235      */
236     public static NodeIterator search(String workspace, String statement) throws InvalidQueryException, RepositoryException {
237         return search(workspace, statement, javax.jcr.query.Query.JCR_SQL2);
238     }
239 
240     /**
241      * Searches for statement and then pops-up in the node hierarchy until returnItemType is found. If the result
242      * is not returnItemType or none of its parents are then next node in result is checked. Duplicate nodes are
243      * removed from result.
244      * For date/time expressions use <code>DateUtil.create*Expression()</code> methods.
245      *
246      * @return query result as collection of nodes
247      */
248     public static NodeIterator search(String workspace, String statement, String language, String returnItemType) throws LoginException, RepositoryException {
249         return search(workspace, statement, language, returnItemType, false);
250     }
251 
252     /**
253      * Searches for statement and then pops-up in the node hierarchy until returnItemType is found. If the result
254      * is not returnItemType or none of its parents are then next node in result is checked. Duplicate nodes are
255      * removed from result.
256      * If isSelector is set to true then returnItemType will be used as the selector and result will contain only nodes
257      * that are marked by this selector.
258      * For date/time expressions use <code>DateUtil.create*Expression()</code> methods.
259      *
260      * @return iterator of nodes
261      */
262     public static NodeIterator search(String workspace, String statement, String language, String returnItemType, boolean isSelector) throws RepositoryException {
263         Session session = MgnlContext.getJCRSession(workspace);
264         QueryManager manager = session.getWorkspace().getQueryManager();
265         Query query = manager.createQuery(statement, language);
266         QueryResult result = query.execute();
267 
268         if (isSelector) {
269             return NodeUtil.filterDuplicates(NodeUtil.filterParentNodeType(result.getRows(), returnItemType));
270         }
271         return NodeUtil.filterDuplicates(NodeUtil.filterParentNodeType(result.getNodes(), returnItemType));
272     }
273 
274     /**
275      * Creates a simple SQL2 query statement.
276      */
277     public static String buildQuery(String statement, String startPath) {
278         Set<String> arguments = new HashSet<String>(Arrays.asList(StringUtils.splitByWholeSeparator(statement, ",")));
279 
280         Iterator<String> argIt = arguments.iterator();
281         String queryString = "select * from [nt:base] as t where ISDESCENDANTNODE([" + startPath + "])";
282         while (argIt.hasNext()) {
283             queryString = queryString + " AND contains(t.*, '" + argIt.next() + "')";
284         }
285         log.debug("query string: {}", queryString);
286         return queryString;
287     }
288 }