View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.web.service.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.krad.lookup.LookupUtils;
20  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
21  import org.kuali.rice.krad.service.ModuleService;
22  import org.kuali.rice.krad.uif.UifConstants;
23  import org.kuali.rice.krad.uif.UifParameters;
24  import org.kuali.rice.krad.uif.UifPropertyPaths;
25  import org.kuali.rice.krad.uif.field.AttributeQueryResult;
26  import org.kuali.rice.krad.uif.service.AttributeQueryService;
27  import org.kuali.rice.krad.util.KRADUtils;
28  import org.kuali.rice.krad.web.form.UifFormBase;
29  import org.kuali.rice.krad.web.service.ModelAndViewService;
30  import org.kuali.rice.krad.web.service.QueryControllerService;
31  import org.springframework.web.servlet.ModelAndView;
32  
33  import javax.servlet.http.HttpServletRequest;
34  import java.util.HashMap;
35  import java.util.Map;
36  import java.util.Properties;
37  
38  /**
39   * Default implementation of the query controller service.
40   *
41   * @author Kuali Rice Team (rice.collab@kuali.org)
42   */
43  public class QueryControllerServiceImpl implements QueryControllerService {
44      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(
45              QueryControllerServiceImpl.class);
46  
47      private ModelAndViewService modelAndViewService;
48      private AttributeQueryService attributeQueryService;
49  
50      /**
51       * Inspects the given request and action parameters on the form to build a URL to the requested
52       * lookup view.
53       *
54       * <p>First the data object class for the lookup view is found from the action parameters. A call
55       * is then made to check if there is a module service that handles that class, and if so the URL from the
56       * module service is used. If not, the base url is and other lookup URL parameters are created from the
57       * action parameters and form.</p>
58       *
59       * {@inheritDoc}
60       *
61       * @see QueryControllerServiceImpl#getLookupDataObjectClass(java.util.Properties)
62       * @see QueryControllerServiceImpl#getLookupUrlFromModuleService(java.lang.Class<?>, java.util.Properties)
63       * @see QueryControllerServiceImpl#buildLookupUrlParameters(org.kuali.rice.krad.web.form.UifFormBase,
64       * javax.servlet.http.HttpServletRequest, java.lang.Class<?>, java.util.Properties)
65       */
66      @Override
67      public ModelAndView performLookup(UifFormBase form) {
68          Properties urlParameters = form.getActionParametersAsProperties();
69  
70          Class<?> lookupDataObjectClass = getLookupDataObjectClass(urlParameters);
71          if (lookupDataObjectClass == null) {
72              throw new RuntimeException("Lookup data object class not found for lookup call");
73          }
74  
75          // Force skip of dirty check
76          urlParameters.put(UifParameters.PERFORM_DIRTY_CHECK, "false");
77  
78          // first give module service the opportunity to build the lookup URL
79          String baseLookupUrl = getLookupUrlFromModuleService(lookupDataObjectClass, urlParameters);
80          if (StringUtils.isNotBlank(baseLookupUrl)) {
81              // url fully built by module service
82              urlParameters = new Properties();
83          } else {
84              baseLookupUrl = urlParameters.getProperty(UifParameters.BASE_LOOKUP_URL);
85              urlParameters.remove(UifParameters.BASE_LOOKUP_URL);
86  
87              buildLookupUrlParameters(form, form.getRequest(), lookupDataObjectClass, urlParameters);
88          }
89  
90          return getModelAndViewService().performRedirect(form, baseLookupUrl, urlParameters);
91      }
92  
93      /**
94       * Returns the Class instance for the data object whose lookup view was requested.
95       *
96       * @param urlParameters properties containing the lookup configuration
97       * @return Class<?> lookup data object class
98       * @throws java.lang.RuntimeException if class cannot be created from data object class name
99       */
100     protected Class<?> getLookupDataObjectClass(Properties urlParameters) {
101         Class<?> lookupDataObjectClass;
102 
103         String lookupObjectClassName = urlParameters.getProperty(UifParameters.DATA_OBJECT_CLASS_NAME);
104         try {
105             lookupDataObjectClass = Class.forName(lookupObjectClassName);
106         } catch (ClassNotFoundException e) {
107             LOG.error("Unable to get class for name: " + lookupObjectClassName);
108             throw new RuntimeException("Unable to get class for name: " + lookupObjectClassName, e);
109         }
110 
111         return lookupDataObjectClass;
112     }
113 
114     /**
115      * Attempts to find a module service that claims responsibility for the given data object class and if
116      * found invokes that module service to build the lookup url.
117      *
118      * @param lookupDataObjectClass data object class to find responsible module service for
119      * @param urlParameters properties containing the lookup configuration
120      * @return String lookup URL returned from module service, or null if not module service was found
121      */
122     protected String getLookupUrlFromModuleService(Class<?> lookupDataObjectClass, Properties urlParameters) {
123         String lookupUrl = null;
124 
125         ModuleService responsibleModuleService =
126                 KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(lookupDataObjectClass);
127         if (responsibleModuleService != null && responsibleModuleService.isExternalizable(lookupDataObjectClass)) {
128             lookupUrl = responsibleModuleService.getExternalizableDataObjectLookupUrl(lookupDataObjectClass,
129                     urlParameters);
130         }
131 
132         return lookupUrl;
133     }
134 
135     /**
136      * Modifies the given properties object representing the lookup URL parameters to add additional parameters
137      * based on the form and action parameters.
138      *
139      * @param form form instance containing the model data
140      * @param request http request object being handled
141      * @param lookupDataObjectClass data object class the lookup URL is being built for
142      * @param urlParameters properties instance holding the lookup URL parameters
143      */
144     protected void buildLookupUrlParameters(UifFormBase form, HttpServletRequest request,
145             Class<?> lookupDataObjectClass, Properties urlParameters) {
146         urlParameters.setProperty(UifParameters.METHOD_TO_CALL, UifConstants.MethodToCallNames.START);
147 
148         String autoSearchString = urlParameters.getProperty(UifParameters.AUTO_SEARCH);
149         if (Boolean.parseBoolean(autoSearchString)) {
150             urlParameters.setProperty(UifParameters.METHOD_TO_CALL, UifConstants.MethodToCallNames.SEARCH);
151         }
152 
153         buildLookupCriteriaParameters(form, request, lookupDataObjectClass, urlParameters);
154 
155         urlParameters.setProperty(UifParameters.RETURN_LOCATION, form.getFormPostUrl());
156         urlParameters.setProperty(UifParameters.RETURN_FORM_KEY, form.getFormKey());       
157     }
158 
159     /**
160      * If lookup criteria parameters were configured, pulls the values for those parameters from the form and
161      * passes as values to pre-populate the lookup view criteria.
162      *
163      * @param form form instance containing the model data
164      * @param request http request object being handled
165      * @param lookupDataObjectClass data object class the lookup URL is being built for
166      * @param urlParameters properties instance holding the lookup URL parameters
167      */
168     protected void buildLookupCriteriaParameters(UifFormBase form, HttpServletRequest request,
169             Class<?> lookupDataObjectClass, Properties urlParameters) {
170         String lookupParameterString = urlParameters.getProperty(UifParameters.LOOKUP_PARAMETERS);
171         if (StringUtils.isBlank(lookupParameterString)) {
172             return;
173         }
174 
175         Map<String, String> lookupParameterFields = KRADUtils.getMapFromParameterString(lookupParameterString);
176         for (Map.Entry<String, String> lookupParameter : lookupParameterFields.entrySet()) {
177             String lookupParameterValue = LookupUtils.retrieveLookupParameterValue(form, request, lookupDataObjectClass,
178                     lookupParameter.getValue(), lookupParameter.getKey());
179 
180             if (StringUtils.isNotBlank(lookupParameterValue)) {
181                 urlParameters.setProperty(UifPropertyPaths.LOOKUP_CRITERIA + "['" + lookupParameter.getValue() + "']",
182                         lookupParameterValue);
183             }
184         }
185 
186         urlParameters.remove(UifParameters.LOOKUP_PARAMETERS);
187     }
188 
189     /**
190      * Retrieves suggest query parameters from the request and invokes
191      * {@link org.kuali.rice.krad.uif.service.AttributeQueryService#performFieldSuggestQuery} to carry out the
192      * suggest query.
193      *
194      * {@inheritDoc}
195      */
196     @Override
197     public AttributeQueryResult performFieldSuggest(UifFormBase form) {
198         HttpServletRequest request = form.getRequest();
199 
200         // retrieve query fields from request
201         Map<String, String> queryParameters = new HashMap<String, String>();
202         for (Object parameterName : request.getParameterMap().keySet()) {
203             if (parameterName.toString().startsWith(UifParameters.QUERY_PARAMETERS)) {
204                 String fieldName = StringUtils.substringBetween(parameterName.toString(),
205                         UifParameters.QUERY_PARAMETERS + "[\"", "\"]");
206                 String fieldValue = request.getParameter(parameterName.toString());
207                 queryParameters.put(fieldName, fieldValue);
208             }
209         }
210 
211         // retrieve id for field to perform query for
212         String queryFieldId = request.getParameter(UifParameters.QUERY_FIELD_ID);
213         if (StringUtils.isBlank(queryFieldId)) {
214             throw new RuntimeException("Unable to find id for field to perform query on under request parameter name: "
215                     + UifParameters.QUERY_FIELD_ID);
216         }
217 
218         // get the field term to match
219         String queryTerm = request.getParameter(UifParameters.QUERY_TERM);
220         if (StringUtils.isBlank(queryTerm)) {
221             throw new RuntimeException(
222                     "Unable to find id for query term value for attribute query on under request parameter name: "
223                             + UifParameters.QUERY_TERM);
224         }
225 
226         return getAttributeQueryService().performFieldSuggestQuery(form.getViewPostMetadata(), queryFieldId, queryTerm,
227                 queryParameters);
228     }
229 
230     /**
231      * Retrieves field query parameters from the request and invokes
232      * {@link org.kuali.rice.krad.uif.service.AttributeQueryService#performFieldQuery} to carry out the
233      * field query.
234      *
235      * {@inheritDoc}
236      */
237     @Override
238     public AttributeQueryResult performFieldQuery(UifFormBase form) {
239         HttpServletRequest request = form.getRequest();
240 
241         // retrieve query fields from request
242         Map<String, String> queryParameters = new HashMap<String, String>();
243         for (Object parameterName : request.getParameterMap().keySet()) {
244             if (parameterName.toString().startsWith(UifParameters.QUERY_PARAMETERS)) {
245                 String fieldName = StringUtils.substringBetween(parameterName.toString(),
246                         UifParameters.QUERY_PARAMETERS + "[\"", "\"]");
247                 String fieldValue = request.getParameter(parameterName.toString());
248                 queryParameters.put(fieldName, fieldValue);
249             }
250         }
251 
252         // retrieve id for field to perform query for
253         String queryFieldId = request.getParameter(UifParameters.QUERY_FIELD_ID);
254         if (StringUtils.isBlank(queryFieldId)) {
255             throw new RuntimeException("Unable to find id for field to perform query on under request parameter name: "
256                     + UifParameters.QUERY_FIELD_ID);
257         }
258 
259         return getAttributeQueryService().performFieldQuery(form.getViewPostMetadata(), queryFieldId, queryParameters);
260     }
261 
262     /**
263      * Instance of model and view service to use within the collection service.
264      *
265      * @return ModelAndViewService instance
266      */
267     protected ModelAndViewService getModelAndViewService() {
268         return modelAndViewService;
269     }
270 
271     /**
272      * @see CollectionControllerServiceImpl#getModelAndViewService()
273      */
274     public void setModelAndViewService(ModelAndViewService modelAndViewService) {
275         this.modelAndViewService = modelAndViewService;
276     }
277 
278     public AttributeQueryService getAttributeQueryService() {
279         return attributeQueryService;
280     }
281 
282     public void setAttributeQueryService(AttributeQueryService attributeQueryService) {
283         this.attributeQueryService = attributeQueryService;
284     }
285 }