001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.xml.xml2json; 029 030import org.opencms.json.JSONException; 031import org.opencms.json.JSONObject; 032import org.opencms.jsp.search.config.parser.CmsSimpleSearchConfigurationParser.SortOption; 033import org.opencms.xml.xml2json.handler.CmsJsonHandlerContainerPage; 034import org.opencms.xml.xml2json.handler.CmsJsonHandlerContext; 035import org.opencms.xml.xml2json.handler.CmsJsonHandlerFolder; 036import org.opencms.xml.xml2json.handler.CmsJsonHandlerList; 037import org.opencms.xml.xml2json.handler.CmsJsonHandlerResource; 038import org.opencms.xml.xml2json.handler.CmsJsonHandlerXmlContent; 039import org.opencms.xml.xml2json.handler.I_CmsJsonHandler; 040 041import java.util.ArrayList; 042import java.util.Arrays; 043import java.util.List; 044 045import javax.servlet.http.HttpServletResponse; 046 047/** 048 * Class representing a JSON request. Provides utility functions for parameter validation. 049 */ 050public class CmsJsonRequest { 051 052 /** The content request parameter. */ 053 public static final String PARAM_CONTENT = "content"; 054 055 /** The fallback locale request parameter. */ 056 public static final String PARAM_FALLBACK_LOCALE = "fallbackLocale"; 057 058 /** The levels request parameter. */ 059 public static final String PARAM_LEVELS = "levels"; 060 061 /** The locale request parameter. */ 062 public static final String PARAM_LOCALE = "locale"; 063 064 /** The path request parameter. */ 065 public static final String PARAM_PATH = "path"; 066 067 /** The rows request parameter. */ 068 public static final String PARAM_ROWS = "rows"; 069 070 /** The sort request parameter. */ 071 public static final String PARAM_SORT = "sort"; 072 073 /** The start request parameter. */ 074 public static final String PARAM_START = "start"; 075 076 /** The wrapper request parameter. */ 077 public static final String PARAM_WRAPPER = "wrapper"; 078 079 /** The JSON handler context. */ 080 CmsJsonHandlerContext m_context; 081 082 /** The JSON handler initiating this request.*/ 083 I_CmsJsonHandler m_handler; 084 085 /** The list of validation errors. */ 086 List<String> errors = new ArrayList<String>(); 087 088 /** 089 * Creates a new JSON request.<p> 090 * 091 * @param context the JSON handler context 092 * @param handler the JSON handler initiating this request 093 */ 094 public CmsJsonRequest(CmsJsonHandlerContext context, I_CmsJsonHandler handler) { 095 096 m_context = context; 097 m_handler = handler; 098 } 099 100 /** 101 * Returns the JSON handler context.<p> 102 * 103 * @return the JSON handler context 104 */ 105 public CmsJsonHandlerContext getContext() { 106 107 return m_context; 108 } 109 110 /** 111 * Returns the errors of this request as JSON.<p> 112 * 113 * @return the errors as JSON 114 * @throws JSONException if JSON rendering fails 115 */ 116 public JSONObject getErrorsAsJson() throws JSONException { 117 118 JSONObject jsonObject = new JSONObject(); 119 jsonObject.put("status", HttpServletResponse.SC_BAD_REQUEST); 120 for (String error : errors) { 121 jsonObject.append("errors", error); 122 } 123 return jsonObject; 124 } 125 126 /** 127 * Returns the boolean parameter value for a given string.<p> 128 * 129 * @param bool the string 130 * @return the boolean 131 */ 132 public Boolean getParamBoolean(String bool) { 133 134 if ((bool == null) || bool.equals("false")) { 135 return Boolean.valueOf(false); 136 } else { 137 return Boolean.valueOf(true); 138 } 139 } 140 141 /** 142 * Returns the content parameter as boolean.<p> 143 * 144 * @return the content parameter as boolean 145 */ 146 public Boolean getParamContent() { 147 148 String paramContent = m_context.getParameters().get(PARAM_CONTENT); 149 return getParamBoolean(paramContent); 150 } 151 152 /** 153 * Returns the fallback locale parameter as boolean.<p> 154 * 155 * @return the fallback locale parameter as boolean 156 */ 157 public Boolean getParamFallbackLocale() { 158 159 String paramFallbackLocale = m_context.getParameters().get(PARAM_FALLBACK_LOCALE); 160 return getParamBoolean(paramFallbackLocale); 161 } 162 163 /** 164 * Returns the levels parameter as integer.<p> 165 * 166 * @return the levels parameter as integer 167 */ 168 public Integer getParamLevels() { 169 170 String paramLevels = m_context.getParameters().get(PARAM_LEVELS); 171 return paramLevels != null ? Integer.valueOf(paramLevels) : null; 172 } 173 174 /** 175 * Returns the levels parameter as integer.<p> 176 * 177 * @param defaultLevels if parameter is not set return this default value 178 * @return the levels parameter as integer 179 */ 180 public Integer getParamLevels(int defaultLevels) { 181 182 Integer levels = getParamLevels(); 183 return levels != null ? levels : Integer.valueOf(defaultLevels); 184 } 185 186 /** 187 * Returns the locale parameter as string.<p> 188 * 189 * @return the levels parameter as integer 190 */ 191 public String getParamLocale() { 192 193 String paramLocale = m_context.getParameters().get(PARAM_LOCALE); 194 return paramLocale; 195 } 196 197 /** 198 * Returns the path parameter as string.<p> 199 * 200 * @return the path parameter as string 201 */ 202 public String getParamPath() { 203 204 String paramPath = m_context.getParameters().get(PARAM_PATH); 205 return paramPath; 206 } 207 208 /** 209 * Returns the rows parameter as integer.<p> 210 * 211 * @return the rows parameter as integer 212 */ 213 public Integer getParamRows() { 214 215 String paramRows = m_context.getParameters().get(PARAM_ROWS); 216 return paramRows != null ? Integer.valueOf(paramRows) : null; 217 } 218 219 /** 220 * Returns the rows parameter as integer.<p> 221 * 222 * @param defaultRows if parameter is not set return this default value 223 * @return the rows parameter as integer 224 */ 225 public Integer getParamRows(int defaultRows) { 226 227 Integer rows = getParamStart(); 228 return rows != null ? rows : Integer.valueOf(defaultRows); 229 } 230 231 /** 232 * Returns the sort parameter as string.<p> 233 * 234 * @return the sort parameter as string 235 */ 236 public String getParamSort() { 237 238 return m_context.getParameters().get(PARAM_SORT); 239 } 240 241 /** 242 * Returns the sort parameter as string.<p> 243 * 244 * @param defaultSort if parameter is not set return this default value 245 * @return the sort parameter as string 246 */ 247 public String getParamSort(String defaultSort) { 248 249 String sort = getParamSort(); 250 return sort != null ? sort : defaultSort; 251 } 252 253 /** 254 * Returns the start parameter as integer.<p> 255 * 256 * @return the start parameter as integer 257 */ 258 public Integer getParamStart() { 259 260 String paramStart = m_context.getParameters().get(PARAM_START); 261 return paramStart != null ? Integer.valueOf(paramStart) : null; 262 } 263 264 /** 265 * Returns the rows parameter as integer.<p> 266 * 267 * @param defaultStart if parameter is not set return this default value 268 * @return the rows parameter as integer 269 */ 270 public Integer getParamStart(int defaultStart) { 271 272 Integer start = getParamStart(); 273 return start != null ? start : Integer.valueOf(defaultStart); 274 } 275 276 /** 277 * Returns the wrapper parameter as string.<p> 278 * 279 * @return the wrapper parameter as string 280 */ 281 public Boolean getParamWrapper() { 282 283 String paramWrapper = m_context.getParameters().get(PARAM_WRAPPER); 284 return getParamBoolean(paramWrapper); 285 } 286 287 /** 288 * Returns the wrapper parameter as string.<p> 289 * 290 * @param defaultWrapper if parameter is not set return this default value 291 * @return the wrapper parameter as string 292 */ 293 public Boolean getParamWrapper(boolean defaultWrapper) { 294 295 String paramRaw = m_context.getParameters().get(PARAM_WRAPPER); 296 if (paramRaw == null) { 297 return Boolean.valueOf(defaultWrapper); 298 } 299 return getParamWrapper(); 300 } 301 302 /** 303 * Whether this JSON request has validation errors. 304 * 305 * @return whether has validation errors or not 306 */ 307 public boolean hasErrors() { 308 309 return !errors.isEmpty(); 310 } 311 312 /** 313 * Validates this request.<p> 314 */ 315 public void validate() { 316 317 validateRequest(); 318 validateDependencies(); 319 validateParamBooleanValue(PARAM_CONTENT, true); 320 validateParamBooleanValue(PARAM_FALLBACK_LOCALE, true); 321 validateParamPositiveIntegerValue(PARAM_LEVELS, false); 322 validateParamPositiveIntegerValue(PARAM_ROWS, false); 323 validateParamSortValue(PARAM_SORT, false); 324 validateParamPositiveIntegerValue(PARAM_START, false); 325 validateParamBooleanValue(PARAM_WRAPPER, true); 326 } 327 328 /** 329 * Validates all parameter dependencies. 330 */ 331 private void validateDependencies() { 332 333 validateParamDepends(PARAM_FALLBACK_LOCALE, PARAM_LOCALE); 334 validateParamDepends(PARAM_PATH, PARAM_LOCALE); 335 validateParamDepends(PARAM_ROWS, PARAM_CONTENT); 336 validateParamDepends(PARAM_SORT, PARAM_CONTENT); 337 validateParamDepends(PARAM_START, PARAM_CONTENT); 338 } 339 340 /** 341 * Validates a boolean request parameter.<p> 342 * 343 * @param paramName the name of the parameter to be validated 344 * @param maybeEmpty whether the parameter value may be empty 345 */ 346 private void validateParamBooleanValue(String paramName, boolean maybeEmpty) { 347 348 String paramValue = m_context.getParameters().get(paramName); 349 if (paramValue != null) { 350 if (maybeEmpty && paramValue.equals("")) { 351 return; 352 } 353 if (!(paramValue.equals("true") || paramValue.equals("false"))) { 354 errors.add( 355 "<" + paramValue + "> is not a boolean. Boolean expected for parameter <" + paramName + ">."); 356 } 357 } 358 } 359 360 /** 361 * For two parameters depending on each other, validates whether the 362 * dependency is fulfilled for the current request. 363 * 364 * @param param the first parameter 365 * @param other the second parameter which the first parameter depends on 366 */ 367 private void validateParamDepends(String param, String other) { 368 369 String first = m_context.getParameters().get(param); 370 String second = m_context.getParameters().get(other); 371 if ((first != null) && (second == null)) { 372 errors.add("Parameter <" + param + "> depends on parameter <" + other + ">. <" + other + "> is expected."); 373 } 374 } 375 376 /** 377 * Validates an integer request parameter.<p> 378 * 379 * @param paramName the name of the parameter to be validated 380 * @param maybeEmpty whether the parameter value may be empty 381 */ 382 private void validateParamPositiveIntegerValue(String paramName, boolean maybeEmpty) { 383 384 String paramValue = m_context.getParameters().get(paramName); 385 if (paramValue != null) { 386 if (maybeEmpty && paramValue.equals("")) { 387 return; 388 } 389 String message = "<" 390 + paramValue 391 + "> is not a positive integer. Positive integer expected for parameter <" 392 + paramName 393 + ">."; 394 try { 395 if (Integer.valueOf(paramValue).intValue() < 0) { 396 errors.add(message); 397 } 398 } catch (NumberFormatException e) { 399 errors.add(message); 400 } 401 } 402 } 403 404 /** 405 * Validates a sort request parameter.<p> 406 * 407 * @param paramName the name of the parameter to be validated 408 * @param maybeEmpty whether the parameter value may be empty 409 */ 410 private void validateParamSortValue(String paramName, boolean maybeEmpty) { 411 412 String paramValue = m_context.getParameters().get(paramName); 413 if (paramValue != null) { 414 if (maybeEmpty && paramValue.equals("")) { 415 return; 416 } 417 List<SortOption> list = Arrays.asList(SortOption.values()); 418 List<String> allowed = new ArrayList<String>(); 419 for (SortOption sortOption : SortOption.values()) { 420 allowed.add(sortOption.toString()); 421 } 422 String message = "<" 423 + paramValue 424 + "> is not a valid sort option. One of <" 425 + String.join(",", allowed) 426 + "> is expected."; 427 try { 428 if (!list.contains(SortOption.valueOf(paramValue))) { 429 errors.add(message); 430 } 431 } catch (Exception e) { 432 errors.add(message); 433 } 434 } 435 } 436 437 /** 438 * For a given list of parameter names supported, validates whether the current 439 * parameters used in this request are valid. 440 * 441 * @param supported the parameters supported 442 */ 443 private void validateParamsSupported(String[] supported) { 444 445 List<String> paramList = Arrays.asList(supported); 446 for (String paramName : m_context.getParameters().keySet()) { 447 if (!paramList.contains(paramName)) { 448 errors.add( 449 "Parameter <" 450 + paramName 451 + "> is not supported for this request type. One of <" 452 + String.join(",", supported) 453 + "> is expected."); 454 } 455 } 456 } 457 458 /** 459 * Validates this request type.<p> 460 */ 461 private void validateRequest() { 462 463 if (m_handler instanceof CmsJsonHandlerFolder) { 464 validateRequestFolder(); 465 } else if (m_handler instanceof CmsJsonHandlerContainerPage) { 466 validateRequestContainerPage(); 467 } else if (m_handler instanceof CmsJsonHandlerList) { 468 validateRequestList(); 469 } else if (m_handler instanceof CmsJsonHandlerXmlContent) { 470 validateRequestXmlContent(); 471 } else if (m_handler instanceof CmsJsonHandlerResource) { 472 validateRequestResource(); 473 } 474 } 475 476 /** 477 * Validates the container page request type.<p> 478 */ 479 private void validateRequestContainerPage() { 480 481 String[] supported = {PARAM_CONTENT, PARAM_WRAPPER, PARAM_LOCALE, PARAM_FALLBACK_LOCALE}; 482 validateParamsSupported(supported); 483 } 484 485 /** 486 * Validates the folder request type.<p> 487 */ 488 private void validateRequestFolder() { 489 490 String[] supported = {PARAM_LEVELS, PARAM_CONTENT, PARAM_WRAPPER, PARAM_LOCALE, PARAM_FALLBACK_LOCALE}; 491 validateParamsSupported(supported); 492 } 493 494 /** 495 * Validates the list request type.<p> 496 */ 497 private void validateRequestList() { 498 499 String[] supported = { 500 PARAM_CONTENT, 501 PARAM_WRAPPER, 502 PARAM_LOCALE, 503 PARAM_FALLBACK_LOCALE, 504 PARAM_START, 505 PARAM_ROWS, 506 PARAM_SORT}; 507 validateParamsSupported(supported); 508 } 509 510 /** 511 * Validates the resource request type.<p> 512 */ 513 private void validateRequestResource() { 514 515 String[] supported = {}; 516 validateParamsSupported(supported); 517 } 518 519 /** 520 * Validates the XML content request type.<p> 521 */ 522 private void validateRequestXmlContent() { 523 524 String[] supported = {PARAM_CONTENT, PARAM_WRAPPER, PARAM_LOCALE, PARAM_FALLBACK_LOCALE, PARAM_PATH}; 525 validateParamsSupported(supported); 526 } 527}