001/*
002 * SonarQube
003 * Copyright (C) 2009-2016 SonarSource SA
004 * mailto:contact 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.annotations.Beta;
023import com.google.common.base.Splitter;
024import com.google.common.collect.Lists;
025import java.io.InputStream;
026import java.util.ArrayList;
027import java.util.Date;
028import java.util.List;
029import javax.annotation.CheckForNull;
030import org.apache.commons.lang.StringUtils;
031import org.sonar.api.utils.DateUtils;
032import org.sonar.api.utils.SonarException;
033
034import static com.google.common.base.Preconditions.checkArgument;
035import static org.sonar.api.utils.DateUtils.parseDateQuietly;
036import static org.sonar.api.utils.DateUtils.parseDateTimeQuietly;
037
038/**
039 * @since 4.2
040 */
041public abstract class Request {
042
043  private static final String MSG_PARAMETER_MISSING = "The '%s' parameter is missing";
044
045  /**
046   * Returns the name of the HTTP method with which this request was made. Possible
047   * values are GET and POST. Others are not supported.
048   */
049  public abstract String method();
050
051  /**
052   * Returns the requested MIME type, or {@code "application/octet-stream"} if not specified.
053   */
054  public abstract String getMediaType();
055
056  /**
057   * Return true of the parameter is set.
058   */
059  public abstract boolean hasParam(String key);
060
061  /**
062   * Returns a non-null value. To be used when parameter is required or has a default value.
063   *
064   * @throws java.lang.IllegalArgumentException is value is null or blank
065   */
066  public String mandatoryParam(String key) {
067    String value = param(key);
068    if (value == null) {
069      throw new IllegalArgumentException(String.format(MSG_PARAMETER_MISSING, key));
070    }
071    return value;
072  }
073
074  /**
075   * Returns a boolean value. To be used when parameter is required or has a default value.
076   *
077   * @throws java.lang.IllegalArgumentException is value is null or blank
078   */
079  public boolean mandatoryParamAsBoolean(String key) {
080    String s = mandatoryParam(key);
081    return parseBoolean(key, s);
082  }
083
084  /**
085   * Returns an int value. To be used when parameter is required or has a default value.
086   *
087   * @throws java.lang.IllegalArgumentException is value is null or blank
088   */
089  public int mandatoryParamAsInt(String key) {
090    String s = mandatoryParam(key);
091    return parseInt(key, s);
092  }
093
094  /**
095   * Returns a long value. To be used when parameter is required or has a default value.
096   *
097   * @throws java.lang.IllegalArgumentException is value is null or blank
098   */
099  public long mandatoryParamAsLong(String key) {
100    String s = mandatoryParam(key);
101    return parseLong(key, s);
102  }
103
104  public <E extends Enum<E>> E mandatoryParamAsEnum(String key, Class<E> enumClass) {
105    return Enum.valueOf(enumClass, mandatoryParam(key));
106  }
107
108  public List<String> mandatoryParamAsStrings(String key) {
109    List<String> values = paramAsStrings(key);
110    if (values == null) {
111      throw new IllegalArgumentException(String.format(MSG_PARAMETER_MISSING, key));
112    }
113    return values;
114  }
115
116  public List<String> mandatoryMultiParam(String key) {
117    List<String> values = multiParam(key);
118    checkArgument(!values.isEmpty(), MSG_PARAMETER_MISSING, key);
119
120    return values;
121  }
122
123  @CheckForNull
124  public List<String> paramAsStrings(String key) {
125    String value = param(key);
126    if (value == null) {
127      return null;
128    }
129    return Lists.newArrayList(Splitter.on(',').omitEmptyStrings().trimResults().split(value));
130  }
131
132  @CheckForNull
133  public abstract String param(String key);
134
135  public abstract List<String> multiParam(String key);
136
137  @CheckForNull
138  public abstract InputStream paramAsInputStream(String key);
139
140  @CheckForNull
141  public abstract Part paramAsPart(String key);
142
143  public Part mandatoryParamAsPart(String key) {
144    Part part = paramAsPart(key);
145    checkArgument(part != null, MSG_PARAMETER_MISSING, key);
146    return part;
147  }
148
149  /**
150   * @deprecated to be dropped in 4.4. Default values are declared in ws metadata
151   */
152  @CheckForNull
153  @Deprecated
154  public String param(String key, @CheckForNull String defaultValue) {
155    return StringUtils.defaultString(param(key), defaultValue);
156  }
157
158  /**
159   * @deprecated to be dropped in 4.4. Default values must be declared in {@link org.sonar.api.server.ws.WebService} then
160   * this method can be replaced by {@link #mandatoryParamAsBoolean(String)}.
161   */
162  @Deprecated
163  public boolean paramAsBoolean(String key, boolean defaultValue) {
164    String value = param(key);
165    return value == null ? defaultValue : parseBoolean(key, value);
166  }
167
168  /**
169   * @deprecated to be dropped in 4.4. Default values must be declared in {@link org.sonar.api.server.ws.WebService} then
170   * this method can be replaced by {@link #mandatoryParamAsInt(String)}.
171   */
172  @Deprecated
173  public int paramAsInt(String key, int defaultValue) {
174    String s = param(key);
175    return s == null ? defaultValue : parseInt(key, s);
176  }
177
178  /**
179   * @deprecated to be dropped in 4.4. Default values must be declared in {@link org.sonar.api.server.ws.WebService} then
180   * this method can be replaced by {@link #mandatoryParamAsLong(String)}.
181   */
182  @Deprecated
183  public long paramAsLong(String key, long defaultValue) {
184    String s = param(key);
185    return s == null ? defaultValue : parseLong(key, s);
186  }
187
188  @CheckForNull
189  public Boolean paramAsBoolean(String key) {
190    String value = param(key);
191    return value == null ? null : parseBoolean(key, value);
192  }
193
194  @CheckForNull
195  public Integer paramAsInt(String key) {
196    String s = param(key);
197    return s == null ? null : parseInt(key, s);
198  }
199
200  @CheckForNull
201  public Long paramAsLong(String key) {
202    String s = param(key);
203    return s == null ? null : parseLong(key, s);
204  }
205
206  @CheckForNull
207  public <E extends Enum<E>> E paramAsEnum(String key, Class<E> enumClass) {
208    String s = param(key);
209    return s == null ? null : Enum.valueOf(enumClass, s);
210  }
211
212  @CheckForNull
213  public <E extends Enum<E>> List<E> paramAsEnums(String key, Class<E> enumClass) {
214    String value = param(key);
215    if (value == null) {
216      return null;
217    }
218    Iterable<String> values = Splitter.on(',').omitEmptyStrings().trimResults().split(value);
219    List<E> result = new ArrayList<>();
220    for (String s : values) {
221      result.add(Enum.valueOf(enumClass, s));
222    }
223
224    return result;
225  }
226
227  @CheckForNull
228  public Date paramAsDateTime(String key) {
229    String stringDate = param(key);
230    if (stringDate == null) {
231      return null;
232    }
233
234    Date date = parseDateTimeQuietly(stringDate);
235    if (date != null) {
236      return date;
237    }
238
239    date = parseDateQuietly(stringDate);
240    checkArgument(date != null, "'%s' cannot be parsed as either a date or date+time", stringDate);
241
242    return date;
243  }
244
245  @CheckForNull
246  public Date paramAsDate(String key) {
247    String s = param(key);
248    if (s == null) {
249      return null;
250    }
251
252    try {
253      return DateUtils.parseDate(s);
254    } catch (SonarException notDateException) {
255      throw new IllegalArgumentException(notDateException);
256    }
257  }
258
259  private static boolean parseBoolean(String key, String value) {
260    if ("true".equals(value) || "yes".equals(value)) {
261      return true;
262    }
263    if ("false".equals(value) || "no".equals(value)) {
264      return false;
265    }
266    throw new IllegalArgumentException(String.format("Property %s is not a boolean value: %s", key, value));
267  }
268
269  private static int parseInt(String key, String value) {
270    try {
271      return Integer.parseInt(value);
272    } catch (NumberFormatException expection) {
273      throw new IllegalArgumentException(String.format("The '%s' parameter cannot be parsed as an integer value: %s", key, value));
274    }
275  }
276
277  private static long parseLong(String key, String value) {
278    try {
279      return Long.parseLong(value);
280    } catch (NumberFormatException expection) {
281      throw new IllegalArgumentException(String.format("The '%s' parameter cannot be parsed as a long value: %s", key, value));
282    }
283  }
284
285  /**
286   * Allows a web service to call another web service.
287   * @see LocalConnector
288   * @since 5.5
289   */
290  @Beta
291  public abstract LocalConnector localConnector();
292
293  /**
294   * Return path of the request
295   * @since 6.0
296   */
297  public abstract String getPath();
298
299  /**
300   * @since 6.0
301   */
302  public interface Part {
303    InputStream getInputStream();
304
305    String getFileName();
306  }
307}