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