001/* 002 * SonarQube 003 * Copyright (C) 2009-2017 SonarSource SA 004 * mailto:info AT sonarsource DOT com 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020package org.sonar.api.server.ws; 021 022import com.google.common.base.Splitter; 023import java.io.InputStream; 024import java.util.ArrayList; 025import java.util.Date; 026import java.util.List; 027import java.util.Optional; 028import java.util.function.BiFunction; 029import java.util.function.Consumer; 030import java.util.function.Supplier; 031import javax.annotation.CheckForNull; 032import javax.annotation.Nullable; 033import org.apache.commons.lang.StringUtils; 034import org.sonar.api.utils.DateUtils; 035 036import static com.google.common.base.Preconditions.checkArgument; 037import static java.lang.String.format; 038import static java.util.Objects.requireNonNull; 039import static org.sonar.api.utils.DateUtils.parseDateQuietly; 040import static org.sonar.api.utils.DateUtils.parseDateTimeQuietly; 041 042/** 043 * @since 4.2 044 */ 045public abstract class Request { 046 047 private static final String MSG_PARAMETER_MISSING = "The '%s' parameter is missing"; 048 049 /** 050 * Returns the name of the HTTP method with which this request was made. Possible 051 * values are GET and POST. Others are not supported. 052 */ 053 public abstract String method(); 054 055 /** 056 * Returns the requested MIME type, or {@code "application/octet-stream"} if not specified. 057 */ 058 public abstract String getMediaType(); 059 060 /** 061 * Return true of the parameter is set in the request. 062 * Does NOT take into account the deprecated key of a parameter. 063 */ 064 public abstract boolean hasParam(String key); 065 066 /** 067 * Returns a non-null value. To be used when parameter is required or has a default value. 068 * 069 * @throws java.lang.IllegalArgumentException is value is null or blank 070 */ 071 public String mandatoryParam(String key) { 072 String value = param(key); 073 checkArgument(value != null, format(MSG_PARAMETER_MISSING, key)); 074 return value; 075 } 076 077 /** 078 * Returns a boolean value. To be used when parameter is required or has a default value. 079 * 080 * @throws java.lang.IllegalArgumentException is value is null or blank 081 */ 082 public boolean mandatoryParamAsBoolean(String key) { 083 String s = mandatoryParam(key); 084 return parseBoolean(key, s); 085 } 086 087 /** 088 * Returns an int value. To be used when parameter is required or has a default value. 089 * 090 * @throws java.lang.IllegalArgumentException is value is null or blank 091 */ 092 public int mandatoryParamAsInt(String key) { 093 String s = mandatoryParam(key); 094 return parseInt(key, s); 095 } 096 097 /** 098 * Returns a long value. To be used when parameter is required or has a default value. 099 * 100 * @throws java.lang.IllegalArgumentException is value is null or blank 101 */ 102 public long mandatoryParamAsLong(String key) { 103 String s = mandatoryParam(key); 104 return parseLong(key, s); 105 } 106 107 public <E extends Enum<E>> E mandatoryParamAsEnum(String key, Class<E> enumClass) { 108 return Enum.valueOf(enumClass, mandatoryParam(key)); 109 } 110 111 public List<String> mandatoryParamAsStrings(String key) { 112 List<String> values = paramAsStrings(key); 113 checkArgument(values != null, format(MSG_PARAMETER_MISSING, key)); 114 return values; 115 } 116 117 public List<String> mandatoryMultiParam(String key) { 118 List<String> values = multiParam(key); 119 checkArgument(!values.isEmpty(), MSG_PARAMETER_MISSING, key); 120 return values; 121 } 122 123 @CheckForNull 124 public abstract List<String> paramAsStrings(String key); 125 126 @CheckForNull 127 public abstract String param(String key); 128 129 public abstract List<String> multiParam(String key); 130 131 @CheckForNull 132 public abstract InputStream paramAsInputStream(String key); 133 134 @CheckForNull 135 public abstract Part paramAsPart(String key); 136 137 public Part mandatoryParamAsPart(String key) { 138 Part part = paramAsPart(key); 139 checkArgument(part != null, MSG_PARAMETER_MISSING, key); 140 return part; 141 } 142 143 /** 144 * @deprecated to be dropped in 4.4. Default values are declared in ws metadata 145 */ 146 @CheckForNull 147 @Deprecated 148 public String param(String key, @CheckForNull String defaultValue) { 149 return StringUtils.defaultString(param(key), defaultValue); 150 } 151 152 /** 153 * @deprecated to be dropped in 4.4. Default values must be declared in {@link org.sonar.api.server.ws.WebService} then 154 * this method can be replaced by {@link #mandatoryParamAsBoolean(String)}. 155 */ 156 @Deprecated 157 public boolean paramAsBoolean(String key, boolean defaultValue) { 158 String value = param(key); 159 return value == null ? defaultValue : parseBoolean(key, value); 160 } 161 162 /** 163 * @deprecated to be dropped in 4.4. Default values must be declared in {@link org.sonar.api.server.ws.WebService} then 164 * this method can be replaced by {@link #mandatoryParamAsInt(String)}. 165 */ 166 @Deprecated 167 public int paramAsInt(String key, int defaultValue) { 168 String s = param(key); 169 return s == null ? defaultValue : parseInt(key, s); 170 } 171 172 /** 173 * @deprecated to be dropped in 4.4. Default values must be declared in {@link org.sonar.api.server.ws.WebService} then 174 * this method can be replaced by {@link #mandatoryParamAsLong(String)}. 175 */ 176 @Deprecated 177 public long paramAsLong(String key, long defaultValue) { 178 String s = param(key); 179 return s == null ? defaultValue : parseLong(key, s); 180 } 181 182 @CheckForNull 183 public Boolean paramAsBoolean(String key) { 184 String value = param(key); 185 return value == null ? null : parseBoolean(key, value); 186 } 187 188 @CheckForNull 189 public Integer paramAsInt(String key) { 190 String s = param(key); 191 return s == null ? null : parseInt(key, s); 192 } 193 194 @CheckForNull 195 public Long paramAsLong(String key) { 196 String s = param(key); 197 return s == null ? null : parseLong(key, s); 198 } 199 200 @CheckForNull 201 public <E extends Enum<E>> E paramAsEnum(String key, Class<E> enumClass) { 202 String s = param(key); 203 return s == null ? null : Enum.valueOf(enumClass, s); 204 } 205 206 @CheckForNull 207 public <E extends Enum<E>> List<E> paramAsEnums(String key, Class<E> enumClass) { 208 String value = param(key); 209 if (value == null) { 210 return null; 211 } 212 Iterable<String> values = Splitter.on(',').omitEmptyStrings().trimResults().split(value); 213 List<E> result = new ArrayList<>(); 214 for (String s : values) { 215 result.add(Enum.valueOf(enumClass, s)); 216 } 217 218 return result; 219 } 220 221 @CheckForNull 222 public Date paramAsDateTime(String key) { 223 String stringDate = param(key); 224 if (stringDate == null) { 225 return null; 226 } 227 228 Date date = parseDateTimeQuietly(stringDate); 229 if (date != null) { 230 return date; 231 } 232 233 date = parseDateQuietly(stringDate); 234 checkArgument(date != null, "'%s' cannot be parsed as either a date or date+time", stringDate); 235 236 return date; 237 } 238 239 @CheckForNull 240 public Date paramAsDate(String key) { 241 String s = param(key); 242 if (s == null) { 243 return null; 244 } 245 246 try { 247 return DateUtils.parseDate(s); 248 } catch (RuntimeException notDateException) { 249 throw new IllegalArgumentException(notDateException); 250 } 251 } 252 253 private static boolean parseBoolean(String key, String value) { 254 if ("true".equals(value) || "yes".equals(value)) { 255 return true; 256 } 257 if ("false".equals(value) || "no".equals(value)) { 258 return false; 259 } 260 throw new IllegalArgumentException(format("Property %s is not a boolean value: %s", key, value)); 261 } 262 263 private static int parseInt(String key, String value) { 264 try { 265 return Integer.parseInt(value); 266 } catch (NumberFormatException expection) { 267 throw new IllegalArgumentException(format("The '%s' parameter cannot be parsed as an integer value: %s", key, value)); 268 } 269 } 270 271 private static long parseLong(String key, String value) { 272 try { 273 return Long.parseLong(value); 274 } catch (NumberFormatException exception) { 275 throw new IllegalArgumentException(format("The '%s' parameter cannot be parsed as a long value: %s", key, value)); 276 } 277 } 278 279 public <T> Param<T> getParam(String key, BiFunction<Request, String, T> retrieveAndValidate) { 280 String param = this.param(key); 281 if (param != null) { 282 return GenericParam.present(retrieveAndValidate.apply(this, key)); 283 } 284 return AbsentParam.absent(); 285 } 286 287 public StringParam getParam(String key, Consumer<String> validate) { 288 String value = this.param(key); 289 if (value != null) { 290 validate.accept(value); 291 return StringParamImpl.present(value); 292 } 293 return AbsentStringParam.absent(); 294 } 295 296 public StringParam getParam(String key) { 297 String value = this.param(key); 298 if (value != null) { 299 return StringParamImpl.present(value); 300 } 301 return AbsentStringParam.absent(); 302 } 303 304 /** 305 * Optional value of the HTTP header with specified name. 306 * If present, the result can have an empty string value ({@code ""}). 307 * 308 * @since 6.6 309 */ 310 public abstract Optional<String> header(String name); 311 312 /** 313 * Allows a web service to call another web service. 314 * @see LocalConnector 315 * @since 5.5 316 */ 317 public abstract LocalConnector localConnector(); 318 319 /** 320 * Return path of the request 321 * @since 6.0 322 */ 323 public abstract String getPath(); 324 325 /** 326 * @since 6.0 327 */ 328 public interface Part { 329 InputStream getInputStream(); 330 331 String getFileName(); 332 } 333 334 /** 335 * Represents a Request parameter, provides information whether is was specified or not (check {@link #isPresent()}) 336 * and utility method to nicely handles cases where the parameter is not present. 337 */ 338 public interface Param<T> { 339 boolean isPresent(); 340 341 /** 342 * @return the value of the parameter 343 * 344 * @throws IllegalStateException if param is not present. 345 */ 346 @CheckForNull 347 T getValue(); 348 349 @CheckForNull 350 T or(Supplier<T> defaultValueSupplier); 351 } 352 353 /** 354 * Implementation of {@link Param} where the param is not present. 355 */ 356 private enum AbsentParam implements Param<Object> { 357 INSTANCE; 358 359 @SuppressWarnings("unchecked") 360 protected static <T> Param<T> absent() { 361 return (Param<T>) INSTANCE; 362 } 363 364 /** 365 * Always returns true. 366 */ 367 @Override 368 public boolean isPresent() { 369 return false; 370 } 371 372 /** 373 * Always throws a {@link IllegalStateException}. 374 */ 375 @Override 376 public Object getValue() { 377 throw createGetValueISE(); 378 } 379 380 /** 381 * Always returns the value supplied by {@code defaultValueSupplier}. 382 */ 383 @Override 384 @CheckForNull 385 public Object or(Supplier<Object> defaultValueSupplier) { 386 return checkDefaultValueSupplier(defaultValueSupplier).get(); 387 } 388 } 389 390 /** 391 * Implementation of {@link Param} where the param is present. 392 */ 393 private static final class GenericParam<T> implements Param<T> { 394 private final T value; 395 396 private GenericParam(T value) { 397 this.value = value; 398 } 399 400 static <T> Param<T> present(T value) { 401 return new GenericParam<>(value); 402 } 403 404 /** 405 * Always returns true. 406 */ 407 @Override 408 public boolean isPresent() { 409 return true; 410 } 411 412 /** 413 * @return the value of the parameter 414 * 415 * @throws IllegalStateException if param is not present. 416 */ 417 @Override 418 @CheckForNull 419 public T getValue() { 420 return value; 421 } 422 423 /** 424 * Always returns value of the parameter. 425 * 426 * @throws NullPointerException As per the inherited contract, {@code defaultValueSupplier} can't be null 427 */ 428 @Override 429 @CheckForNull 430 public T or(Supplier<T> defaultValueSupplier) { 431 checkDefaultValueSupplier(defaultValueSupplier); 432 return value; 433 } 434 } 435 436 /** 437 * Extends {@link Param} with convenience methods specific to the type {@link String}. 438 */ 439 public interface StringParam extends Param<String> { 440 /** 441 * Returns a {@link StringParam} object which methods {@link #getValue()} and {@link #or(Supplier)} will 442 * return {@code null} rather than an empty String when the param is present and its value is an empty String. 443 */ 444 StringParam emptyAsNull(); 445 } 446 447 /** 448 * Implementation of {@link StringParam} where the param is not present. 449 */ 450 private enum AbsentStringParam implements StringParam { 451 INSTANCE; 452 453 protected static StringParam absent() { 454 return INSTANCE; 455 } 456 457 /** 458 * Always returns false. 459 */ 460 @Override 461 public boolean isPresent() { 462 return false; 463 } 464 465 /** 466 * Always throws a {@link IllegalStateException}. 467 */ 468 @Override 469 public String getValue() { 470 throw createGetValueISE(); 471 } 472 473 /** 474 * Always returns the value supplied by {@code defaultValueSupplier}. 475 */ 476 @Override 477 public String or(Supplier<String> defaultValueSupplier) { 478 return checkDefaultValueSupplier(defaultValueSupplier).get(); 479 } 480 481 /** 482 * Returns itself. 483 */ 484 @Override 485 public StringParam emptyAsNull() { 486 return this; 487 } 488 } 489 490 /** 491 * Implementation of {@link StringParam} where the param is present. 492 */ 493 private static final class StringParamImpl implements StringParam { 494 @CheckForNull 495 private final String value; 496 private final boolean emptyAsNull; 497 498 private StringParamImpl(@Nullable String value, boolean emptyAsNull) { 499 this.value = value; 500 this.emptyAsNull = emptyAsNull; 501 } 502 503 static StringParam present(String value) { 504 return new StringParamImpl(value, false); 505 } 506 507 @Override 508 public boolean isPresent() { 509 return true; 510 } 511 512 @Override 513 public String getValue() { 514 if (emptyAsNull && value != null && value.isEmpty()) { 515 return null; 516 } 517 return value; 518 } 519 520 @Override 521 @CheckForNull 522 public String or(Supplier<String> defaultValueSupplier) { 523 checkDefaultValueSupplier(defaultValueSupplier); 524 if (emptyAsNull && value != null && value.isEmpty()) { 525 return null; 526 } 527 return value; 528 } 529 530 @Override 531 public StringParam emptyAsNull() { 532 if (emptyAsNull || (value != null && !value.isEmpty())) { 533 return this; 534 } 535 return new StringParamImpl(value, true); 536 } 537 } 538 539 private static <T> Supplier<T> checkDefaultValueSupplier(Supplier<T> defaultValueSupplier) { 540 return requireNonNull(defaultValueSupplier, "default value supplier can't be null"); 541 } 542 543 private static IllegalStateException createGetValueISE() { 544 return new IllegalStateException("Param has no value. Use isPresent() before calling getValue()"); 545 } 546}