001/* 002 * (C) Copyright 2006-2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Thierry Delprat 018 */ 019package org.nuxeo.ecm.automation.core.operations.services; 020 021import java.io.Serializable; 022import java.util.ArrayList; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.commons.lang3.StringUtils; 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030import org.nuxeo.ecm.automation.OperationContext; 031import org.nuxeo.ecm.automation.OperationException; 032import org.nuxeo.ecm.automation.core.Constants; 033import org.nuxeo.ecm.automation.core.annotations.Context; 034import org.nuxeo.ecm.automation.core.annotations.Operation; 035import org.nuxeo.ecm.automation.core.annotations.OperationMethod; 036import org.nuxeo.ecm.automation.core.annotations.Param; 037import org.nuxeo.ecm.automation.core.util.Properties; 038import org.nuxeo.ecm.automation.core.util.RecordSet; 039import org.nuxeo.ecm.automation.core.util.StringList; 040import org.nuxeo.ecm.core.api.CoreSession; 041import org.nuxeo.ecm.core.api.DocumentModel; 042import org.nuxeo.ecm.core.api.SortInfo; 043import org.nuxeo.ecm.core.query.sql.NXQL; 044import org.nuxeo.ecm.platform.query.api.PageProvider; 045import org.nuxeo.ecm.platform.query.api.PageProviderService; 046import org.nuxeo.ecm.platform.query.core.GenericPageProviderDescriptor; 047import org.nuxeo.ecm.platform.query.nxql.CoreQueryAndFetchPageProvider; 048import org.nuxeo.ecm.platform.query.nxql.CoreQueryDocumentPageProvider; 049 050/** 051 * Operation to execute a query or a named provider with support for Pagination 052 * 053 * @author Tiry (tdelprat@nuxeo.com) 054 * @since 5.7 055 */ 056@Operation(id = ResultSetPageProviderOperation.ID, category = Constants.CAT_FETCH, label = "QueryAndFetch", description = "Perform " 057 + "a query or a named provider query on the repository. Result is " 058 + "paginated. The result is returned as a RecordSet (QueryAndFetch) " + "rather than as a List of Document" 059 + "The query result will become the input for the next " 060 + "operation. If no query or provider name is given, a query returning " 061 + "all the documents that the user has access to will be executed.", addToStudio = false, aliases = { 062 "Resultset.PageProvider" }) 063public class ResultSetPageProviderOperation { 064 065 private static final Log log = LogFactory.getLog(ResultSetPageProviderOperation.class); 066 067 public static final String ID = "Repository.ResultSetPageProvider"; 068 069 public static final String CURRENT_USERID_PATTERN = "$currentUser"; 070 071 public static final String CURRENT_REPO_PATTERN = "$currentRepository"; 072 073 public static final String DESC = "DESC"; 074 075 public static final String ASC = "ASC"; 076 077 public static final String CMIS = "CMIS"; 078 079 @Context 080 protected OperationContext context; 081 082 @Context 083 protected CoreSession session; 084 085 @Context 086 protected PageProviderService ppService; 087 088 @Param(name = "providerName", required = false) 089 protected String providerName; 090 091 /** 092 * @deprecated since 6.0 use instead {@link org.nuxeo.ecm.automation .core.operations.services.query.ResultSetQuery} 093 */ 094 @Deprecated 095 @Param(name = "query", required = false) 096 protected String query; 097 098 @Param(name = "language", required = false, widget = Constants.W_OPTION, values = { NXQL.NXQL, CMIS }) 099 protected String lang = NXQL.NXQL; 100 101 @Param(name = "page", required = false) 102 protected Integer page; 103 104 @Param(name = "pageSize", required = false) 105 protected Integer pageSize; 106 107 /** 108 * @deprecated since 6.0 use instead {@link #sortBy and @link #sortOrder} 109 */ 110 @Deprecated 111 @Param(name = "sortInfo", required = false) 112 protected StringList sortInfoAsStringList; 113 114 @Param(name = "queryParams", required = false) 115 protected StringList strParameters; 116 117 @Param(name = "documentLinkBuilder", required = false) 118 protected String documentLinkBuilder; 119 120 /** 121 * @since 5.7 122 */ 123 @Param(name = "maxResults", required = false) 124 protected String maxResults = "100"; 125 126 /** 127 * @since 6.0 128 */ 129 @Param(name = PageProviderService.NAMED_PARAMETERS, required = false, description = "Named parameters to pass to the page provider to " 130 + "fill in query variables.") 131 protected Properties namedParameters; 132 133 /** 134 * @since 6.0 135 */ 136 @Param(name = "sortBy", required = false, description = "Sort by " + "properties (separated by comma)") 137 protected String sortBy; 138 139 /** 140 * @since 6.0 141 */ 142 @Param(name = "sortOrder", required = false, description = "Sort order, " 143 + "ASC or DESC", widget = Constants.W_OPTION, values = { ASC, DESC }) 144 protected String sortOrder; 145 146 @SuppressWarnings("unchecked") 147 @OperationMethod 148 public RecordSet run() throws OperationException { 149 150 List<SortInfo> sortInfos = null; 151 if (sortInfoAsStringList != null) { 152 sortInfos = new ArrayList<>(); 153 for (String sortInfoDesc : sortInfoAsStringList) { 154 SortInfo sortInfo; 155 if (sortInfoDesc.contains("|")) { 156 String[] parts = sortInfoDesc.split("\\|"); 157 sortInfo = new SortInfo(parts[0], Boolean.parseBoolean(parts[1])); 158 } else { 159 sortInfo = new SortInfo(sortInfoDesc, true); 160 } 161 sortInfos.add(sortInfo); 162 } 163 } else { 164 // Sort Info Management 165 if (!StringUtils.isBlank(sortBy)) { 166 sortInfos = new ArrayList<>(); 167 String[] sorts = sortBy.split(","); 168 String[] orders = null; 169 if (!StringUtils.isBlank(sortOrder)) { 170 orders = sortOrder.split(","); 171 } 172 for (int i = 0; i < sorts.length; i++) { 173 String sort = sorts[i]; 174 boolean sortAscending = (orders != null && orders.length > i 175 && "asc".equals(orders[i].toLowerCase())); 176 sortInfos.add(new SortInfo(sort, sortAscending)); 177 } 178 } 179 } 180 181 Object[] parameters = null; 182 183 if (strParameters != null && !strParameters.isEmpty()) { 184 parameters = strParameters.toArray(new String[strParameters.size()]); 185 // expand specific parameters 186 for (int idx = 0; idx < parameters.length; idx++) { 187 String value = (String) parameters[idx]; 188 if (value.equals(CURRENT_USERID_PATTERN)) { 189 parameters[idx] = session.getPrincipal().getName(); 190 } else if (value.equals(CURRENT_REPO_PATTERN)) { 191 parameters[idx] = session.getRepositoryName(); 192 } 193 } 194 } 195 196 Map<String, Serializable> props = new HashMap<>(); 197 props.put(CoreQueryDocumentPageProvider.CORE_SESSION_PROPERTY, (Serializable) session); 198 199 if (query == null && StringUtils.isEmpty(providerName)) { 200 // provide a defaut query 201 query = "SELECT * from Document"; 202 } 203 204 Long targetPage = null; 205 if (page != null) { 206 targetPage = page.longValue(); 207 } 208 Long targetPageSize = null; 209 if (pageSize != null) { 210 targetPageSize = pageSize.longValue(); 211 } 212 213 DocumentModel searchDocumentModel = DocumentPageProviderOperation.getSearchDocumentModel(session, ppService, 214 providerName, namedParameters); 215 216 final class QueryAndFetchProviderDescriptor extends GenericPageProviderDescriptor { 217 private static final long serialVersionUID = 1L; 218 219 public QueryAndFetchProviderDescriptor() { 220 super(); 221 try { 222 klass = (Class<PageProvider<?>>) Class.forName(CoreQueryAndFetchPageProvider.class.getName()); 223 } catch (ClassNotFoundException e) { 224 log.error(e, e); 225 } 226 } 227 } 228 229 PageProvider<Map<String, Serializable>> pp; 230 if (query != null) { 231 QueryAndFetchProviderDescriptor desc = new QueryAndFetchProviderDescriptor(); 232 desc.setPattern(query); 233 if (maxResults != null && !maxResults.isEmpty() && !maxResults.equals("-1")) { 234 // set the maxResults to avoid slowing down queries 235 desc.getProperties().put("maxResults", maxResults); 236 } 237 pp = (CoreQueryAndFetchPageProvider) ppService.getPageProvider("", desc, searchDocumentModel, sortInfos, 238 targetPageSize, targetPage, props, parameters); 239 } else { 240 pp = (PageProvider<Map<String, Serializable>>) ppService.getPageProvider(providerName, searchDocumentModel, 241 sortInfos, targetPageSize, targetPage, props, parameters); 242 } 243 PaginableRecordSetImpl res = new PaginableRecordSetImpl(pp); 244 if (res.hasError()) { 245 throw new OperationException(res.getErrorMessage()); 246 } 247 return res; 248 249 } 250}