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.internal; 021 022import com.google.common.base.CharMatcher; 023import com.google.common.base.Splitter; 024import java.io.InputStream; 025import java.util.List; 026import java.util.Set; 027import java.util.stream.Collectors; 028import javax.annotation.CheckForNull; 029import javax.annotation.Nullable; 030import org.sonar.api.server.ws.LocalConnector; 031import org.sonar.api.server.ws.Request; 032import org.sonar.api.server.ws.WebService; 033 034import static com.google.common.base.Preconditions.checkArgument; 035import static java.util.Collections.emptyList; 036import static java.util.Collections.singletonList; 037import static java.util.Objects.requireNonNull; 038import static org.apache.commons.lang.StringUtils.defaultString; 039 040/** 041 * @since 4.2 042 */ 043public abstract class ValidatingRequest extends Request { 044 045 private static final Splitter COMMA_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults(); 046 private WebService.Action action; 047 private LocalConnector localConnector; 048 049 public void setAction(WebService.Action action) { 050 this.action = action; 051 } 052 053 public WebService.Action action() { 054 return action; 055 } 056 057 @Override 058 public LocalConnector localConnector() { 059 requireNonNull(localConnector, "Local connector has not been set"); 060 return localConnector; 061 } 062 063 public void setLocalConnector(LocalConnector lc) { 064 this.localConnector = lc; 065 } 066 067 @Override 068 @CheckForNull 069 public String param(String key) { 070 return param(key, true); 071 } 072 073 @Override 074 public List<String> multiParam(String key) { 075 WebService.Param definition = action.param(key); 076 List<String> values = readMultiParamOrDefaultValue(key, definition); 077 return validateValues(values, definition); 078 } 079 080 @Override 081 @CheckForNull 082 public InputStream paramAsInputStream(String key) { 083 return readInputStreamParam(key); 084 } 085 086 @Override 087 @CheckForNull 088 public Part paramAsPart(String key) { 089 return readPart(key); 090 } 091 092 @CheckForNull 093 private String param(String key, boolean validateValue) { 094 WebService.Param definition = action.param(key); 095 String value = readParamOrDefaultValue(key, definition); 096 String trimmedValue = value == null ? null : CharMatcher.WHITESPACE.trimFrom(value); 097 if (trimmedValue != null && validateValue) { 098 validateValue(trimmedValue, definition); 099 } 100 return trimmedValue; 101 } 102 103 @CheckForNull 104 @Override 105 public List<String> paramAsStrings(String key) { 106 WebService.Param definition = action.param(key); 107 String value = readParamOrDefaultValue(key, definition); 108 if (value == null) { 109 return null; 110 } 111 List<String> values = COMMA_SPLITTER.splitToList(value); 112 return validateValues(values, definition); 113 } 114 115 @CheckForNull 116 @Override 117 public <E extends Enum<E>> List<E> paramAsEnums(String key, Class<E> enumClass) { 118 List<String> values = paramAsStrings(key); 119 if (values == null) { 120 return null; 121 } 122 return values.stream() 123 .map(value -> Enum.valueOf(enumClass, value)) 124 .collect(Collectors.toList()); 125 } 126 127 @CheckForNull 128 private String readParamOrDefaultValue(String key, @Nullable WebService.Param definition) { 129 checkArgument(definition != null, "BUG - parameter '%s' is undefined for action '%s'", key, action.key()); 130 131 String deprecatedKey = definition.deprecatedKey(); 132 String value = deprecatedKey != null ? defaultString(readParam(deprecatedKey), readParam(key)) : readParam(key); 133 return defaultString(value, definition.defaultValue()); 134 } 135 136 private List<String> readMultiParamOrDefaultValue(String key, @Nullable WebService.Param definition) { 137 checkArgument(definition != null, "BUG - parameter '%s' is undefined for action '%s'", key, action.key()); 138 139 List<String> keyValues = readMultiParam(key); 140 if (!keyValues.isEmpty()) { 141 return keyValues; 142 } 143 144 String deprecatedKey = definition.deprecatedKey(); 145 List<String> deprecatedKeyValues = deprecatedKey == null ? emptyList() : readMultiParam(deprecatedKey); 146 if (!deprecatedKeyValues.isEmpty()) { 147 return deprecatedKeyValues; 148 } 149 150 String defaultValue = definition.defaultValue(); 151 return defaultValue == null ? emptyList() : singletonList(defaultValue); 152 } 153 154 @CheckForNull 155 protected abstract String readParam(String key); 156 157 protected abstract List<String> readMultiParam(String key); 158 159 @CheckForNull 160 protected abstract InputStream readInputStreamParam(String key); 161 162 @CheckForNull 163 protected abstract Part readPart(String key); 164 165 private static List<String> validateValues(List<String> values, WebService.Param definition) { 166 Integer maximumValues = definition.maxValuesAllowed(); 167 checkArgument(maximumValues == null || values.size() <= maximumValues, "'%s' can contains only %s values, got %s", definition.key(), maximumValues, values.size()); 168 values.forEach(value -> validateValue(value, definition)); 169 return values; 170 } 171 172 private static void validateValue(String value, WebService.Param definition) { 173 Set<String> possibleValues = definition.possibleValues(); 174 checkArgument(possibleValues == null || possibleValues.contains(value), "Value of parameter '%s' (%s) must be one of: %s", definition.key(), value, possibleValues); 175 } 176}