You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Request.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.api.server.ws;
  21. import java.io.BufferedReader;
  22. import java.io.InputStream;
  23. import java.util.Arrays;
  24. import java.util.Collections;
  25. import java.util.Date;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.Optional;
  29. import java.util.function.BiFunction;
  30. import java.util.function.Consumer;
  31. import java.util.function.Supplier;
  32. import java.util.stream.Collectors;
  33. import javax.annotation.CheckForNull;
  34. import javax.annotation.Nullable;
  35. import org.sonar.api.utils.DateUtils;
  36. import static java.lang.String.format;
  37. import static java.util.Objects.requireNonNull;
  38. import static org.sonar.api.utils.DateUtils.parseDateQuietly;
  39. import static org.sonar.api.utils.DateUtils.parseDateTimeQuietly;
  40. import static org.sonar.api.utils.Preconditions.checkArgument;
  41. /**
  42. * @since 4.2
  43. */
  44. public abstract class Request {
  45. protected static final String MSG_PARAMETER_MISSING = "The '%s' parameter is missing";
  46. /**
  47. * Returns the name of the HTTP method with which this request was made. Possible
  48. * values are GET and POST. Others are not supported.
  49. */
  50. public abstract String method();
  51. public BufferedReader getReader() {
  52. throw new UnsupportedOperationException("getReader not supported");
  53. }
  54. /**
  55. * Returns the requested MIME type, or {@code "application/octet-stream"} if not specified.
  56. */
  57. public abstract String getMediaType();
  58. /**
  59. * Return true of the parameter is set in the request.
  60. * Does NOT take into account the deprecated key of a parameter.
  61. */
  62. public abstract boolean hasParam(String key);
  63. /**
  64. * Returns a non-null value. To be used when parameter is required or has a default value.
  65. *
  66. * @throws java.lang.IllegalArgumentException is value is null or blank
  67. */
  68. public String mandatoryParam(String key) {
  69. String value = param(key);
  70. checkArgument(value != null && !value.isEmpty(), format(MSG_PARAMETER_MISSING, key));
  71. return value;
  72. }
  73. /**
  74. * Returns a boolean value. To be used when parameter is required or has a default value.
  75. *
  76. * @throws java.lang.IllegalArgumentException is value is null or blank
  77. */
  78. public boolean mandatoryParamAsBoolean(String key) {
  79. String s = mandatoryParam(key);
  80. return parseBoolean(key, s);
  81. }
  82. /**
  83. * Returns an int value. To be used when parameter is required or has a default value.
  84. *
  85. * @throws java.lang.IllegalArgumentException is value is null or blank
  86. */
  87. public int mandatoryParamAsInt(String key) {
  88. String s = mandatoryParam(key);
  89. return parseInt(key, s);
  90. }
  91. /**
  92. * Returns a long value. To be used when parameter is required or has a default value.
  93. *
  94. * @throws java.lang.IllegalArgumentException is value is null or blank
  95. */
  96. public long mandatoryParamAsLong(String key) {
  97. String s = mandatoryParam(key);
  98. return parseLong(key, s);
  99. }
  100. public <E extends Enum<E>> E mandatoryParamAsEnum(String key, Class<E> enumClass) {
  101. return Enum.valueOf(enumClass, mandatoryParam(key));
  102. }
  103. public List<String> mandatoryParamAsStrings(String key) {
  104. List<String> values = paramAsStrings(key);
  105. checkArgument(values != null, format(MSG_PARAMETER_MISSING, key));
  106. return values;
  107. }
  108. public List<String> mandatoryMultiParam(String key) {
  109. List<String> values = multiParam(key);
  110. checkArgument(!values.isEmpty(), MSG_PARAMETER_MISSING, key);
  111. return values;
  112. }
  113. @CheckForNull
  114. public abstract List<String> paramAsStrings(String key);
  115. public abstract Map<String, String[]> getParams();
  116. @CheckForNull
  117. public abstract String param(String key);
  118. public abstract List<String> multiParam(String key);
  119. @CheckForNull
  120. public abstract InputStream paramAsInputStream(String key);
  121. @CheckForNull
  122. public abstract Part paramAsPart(String key);
  123. public Part mandatoryParamAsPart(String key) {
  124. Part part = paramAsPart(key);
  125. checkArgument(part != null, MSG_PARAMETER_MISSING, key);
  126. return part;
  127. }
  128. @CheckForNull
  129. public Boolean paramAsBoolean(String key) {
  130. String value = param(key);
  131. return value == null ? null : parseBoolean(key, value);
  132. }
  133. @CheckForNull
  134. public Integer paramAsInt(String key) {
  135. String s = param(key);
  136. return s == null ? null : parseInt(key, s);
  137. }
  138. @CheckForNull
  139. public Long paramAsLong(String key) {
  140. String s = param(key);
  141. return s == null ? null : parseLong(key, s);
  142. }
  143. @CheckForNull
  144. public <E extends Enum<E>> E paramAsEnum(String key, Class<E> enumClass) {
  145. String s = param(key);
  146. return s == null ? null : Enum.valueOf(enumClass, s);
  147. }
  148. @CheckForNull
  149. public <E extends Enum<E>> List<E> paramAsEnums(String key, Class<E> enumClass) {
  150. String value = param(key);
  151. if (value == null) {
  152. return null;
  153. }
  154. return Arrays.stream(value.split(","))
  155. .map(String::trim)
  156. .filter(s -> !s.isEmpty())
  157. .map(x -> Enum.valueOf(enumClass, x))
  158. .collect(Collectors.toList());
  159. }
  160. @CheckForNull
  161. public Date paramAsDateTime(String key) {
  162. String stringDate = param(key);
  163. if (stringDate == null) {
  164. return null;
  165. }
  166. Date date = parseDateTimeQuietly(stringDate);
  167. if (date != null) {
  168. return date;
  169. }
  170. date = parseDateQuietly(stringDate);
  171. checkArgument(date != null, "'%s' cannot be parsed as either a date or date+time", stringDate);
  172. return date;
  173. }
  174. @CheckForNull
  175. public Date paramAsDate(String key) {
  176. String s = param(key);
  177. if (s == null) {
  178. return null;
  179. }
  180. try {
  181. return DateUtils.parseDate(s);
  182. } catch (RuntimeException notDateException) {
  183. throw new IllegalArgumentException(notDateException);
  184. }
  185. }
  186. private static boolean parseBoolean(String key, String value) {
  187. if ("true".equals(value) || "yes".equals(value)) {
  188. return true;
  189. }
  190. if ("false".equals(value) || "no".equals(value)) {
  191. return false;
  192. }
  193. throw new IllegalArgumentException(format("Property %s is not a boolean value: %s", key, value));
  194. }
  195. private static int parseInt(String key, String value) {
  196. try {
  197. return Integer.parseInt(value);
  198. } catch (NumberFormatException expection) {
  199. throw new IllegalArgumentException(format("The '%s' parameter cannot be parsed as an integer value: %s", key, value));
  200. }
  201. }
  202. private static long parseLong(String key, String value) {
  203. try {
  204. return Long.parseLong(value);
  205. } catch (NumberFormatException exception) {
  206. throw new IllegalArgumentException(format("The '%s' parameter cannot be parsed as a long value: %s", key, value));
  207. }
  208. }
  209. public <T> Param<T> getParam(String key, BiFunction<Request, String, T> retrieveAndValidate) {
  210. String param = this.param(key);
  211. if (param != null) {
  212. return GenericParam.present(retrieveAndValidate.apply(this, key));
  213. }
  214. return AbsentParam.absent();
  215. }
  216. public StringParam getParam(String key, Consumer<String> validate) {
  217. String value = this.param(key);
  218. if (value != null) {
  219. validate.accept(value);
  220. return StringParamImpl.present(value);
  221. }
  222. return AbsentStringParam.absent();
  223. }
  224. public StringParam getParam(String key) {
  225. String value = this.param(key);
  226. if (value != null) {
  227. return StringParamImpl.present(value);
  228. }
  229. return AbsentStringParam.absent();
  230. }
  231. /**
  232. * Optional value of the HTTP header with specified name.
  233. * If present, the result can have an empty string value ({@code ""}).
  234. *
  235. * @since 6.6
  236. */
  237. public abstract Optional<String> header(String name);
  238. public Map<String, String> getHeaders() {
  239. return Collections.emptyMap();
  240. }
  241. /**
  242. * Allows a web service to call another web service.
  243. *
  244. * @see LocalConnector
  245. * @since 5.5
  246. */
  247. public abstract LocalConnector localConnector();
  248. /**
  249. * Return path of the request
  250. *
  251. * @since 6.0
  252. */
  253. public abstract String getPath();
  254. /**
  255. * @since 6.0
  256. */
  257. public interface Part {
  258. InputStream getInputStream();
  259. String getFileName();
  260. }
  261. /**
  262. * Represents a Request parameter, provides information whether is was specified or not (check {@link #isPresent()})
  263. * and utility method to nicely handles cases where the parameter is not present.
  264. */
  265. public interface Param<T> {
  266. boolean isPresent();
  267. /**
  268. * @return the value of the parameter
  269. * @throws IllegalStateException if param is not present.
  270. */
  271. @CheckForNull
  272. T getValue();
  273. @CheckForNull
  274. T or(Supplier<T> defaultValueSupplier);
  275. }
  276. /**
  277. * Implementation of {@link Param} where the param is not present.
  278. */
  279. private enum AbsentParam implements Param<Object> {
  280. INSTANCE;
  281. @SuppressWarnings("unchecked")
  282. protected static <T> Param<T> absent() {
  283. return (Param<T>) INSTANCE;
  284. }
  285. /**
  286. * Always returns true.
  287. */
  288. @Override
  289. public boolean isPresent() {
  290. return false;
  291. }
  292. /**
  293. * Always throws a {@link IllegalStateException}.
  294. */
  295. @Override
  296. public Object getValue() {
  297. throw createGetValueISE();
  298. }
  299. /**
  300. * Always returns the value supplied by {@code defaultValueSupplier}.
  301. */
  302. @Override
  303. @CheckForNull
  304. public Object or(Supplier<Object> defaultValueSupplier) {
  305. return checkDefaultValueSupplier(defaultValueSupplier).get();
  306. }
  307. }
  308. /**
  309. * Implementation of {@link Param} where the param is present.
  310. */
  311. private static final class GenericParam<T> implements Param<T> {
  312. private final T value;
  313. private GenericParam(T value) {
  314. this.value = value;
  315. }
  316. static <T> Param<T> present(T value) {
  317. return new GenericParam<>(value);
  318. }
  319. /**
  320. * Always returns true.
  321. */
  322. @Override
  323. public boolean isPresent() {
  324. return true;
  325. }
  326. /**
  327. * @return the value of the parameter
  328. * @throws IllegalStateException if param is not present.
  329. */
  330. @Override
  331. @CheckForNull
  332. public T getValue() {
  333. return value;
  334. }
  335. /**
  336. * Always returns value of the parameter.
  337. *
  338. * @throws NullPointerException As per the inherited contract, {@code defaultValueSupplier} can't be null
  339. */
  340. @Override
  341. @CheckForNull
  342. public T or(Supplier<T> defaultValueSupplier) {
  343. checkDefaultValueSupplier(defaultValueSupplier);
  344. return value;
  345. }
  346. }
  347. /**
  348. * Extends {@link Param} with convenience methods specific to the type {@link String}.
  349. */
  350. public interface StringParam extends Param<String> {
  351. /**
  352. * Returns a {@link StringParam} object which methods {@link #getValue()} and {@link #or(Supplier)} will
  353. * return {@code null} rather than an empty String when the param is present and its value is an empty String.
  354. */
  355. StringParam emptyAsNull();
  356. }
  357. /**
  358. * Implementation of {@link StringParam} where the param is not present.
  359. */
  360. private enum AbsentStringParam implements StringParam {
  361. INSTANCE;
  362. protected static StringParam absent() {
  363. return INSTANCE;
  364. }
  365. /**
  366. * Always returns false.
  367. */
  368. @Override
  369. public boolean isPresent() {
  370. return false;
  371. }
  372. /**
  373. * Always throws a {@link IllegalStateException}.
  374. */
  375. @Override
  376. public String getValue() {
  377. throw createGetValueISE();
  378. }
  379. /**
  380. * Always returns the value supplied by {@code defaultValueSupplier}.
  381. */
  382. @Override
  383. public String or(Supplier<String> defaultValueSupplier) {
  384. return checkDefaultValueSupplier(defaultValueSupplier).get();
  385. }
  386. /**
  387. * Returns itself.
  388. */
  389. @Override
  390. public StringParam emptyAsNull() {
  391. return this;
  392. }
  393. }
  394. /**
  395. * Implementation of {@link StringParam} where the param is present.
  396. */
  397. private static final class StringParamImpl implements StringParam {
  398. @CheckForNull
  399. private final String value;
  400. private final boolean emptyAsNull;
  401. private StringParamImpl(@Nullable String value, boolean emptyAsNull) {
  402. this.value = value;
  403. this.emptyAsNull = emptyAsNull;
  404. }
  405. static StringParam present(String value) {
  406. return new StringParamImpl(value, false);
  407. }
  408. @Override
  409. public boolean isPresent() {
  410. return true;
  411. }
  412. @Override
  413. public String getValue() {
  414. if (emptyAsNull && value != null && value.isEmpty()) {
  415. return null;
  416. }
  417. return value;
  418. }
  419. @Override
  420. @CheckForNull
  421. public String or(Supplier<String> defaultValueSupplier) {
  422. checkDefaultValueSupplier(defaultValueSupplier);
  423. if (emptyAsNull && value != null && value.isEmpty()) {
  424. return null;
  425. }
  426. return value;
  427. }
  428. @Override
  429. public StringParam emptyAsNull() {
  430. if (emptyAsNull || (value != null && !value.isEmpty())) {
  431. return this;
  432. }
  433. return new StringParamImpl(value, true);
  434. }
  435. }
  436. private static <T> Supplier<T> checkDefaultValueSupplier(Supplier<T> defaultValueSupplier) {
  437. return requireNonNull(defaultValueSupplier, "default value supplier can't be null");
  438. }
  439. private static IllegalStateException createGetValueISE() {
  440. return new IllegalStateException("Param has no value. Use isPresent() before calling getValue()");
  441. }
  442. }