blob: 32adaa59eb8452c48b81d17762dafaed70ca7005 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ambari.server.api.services;
import org.apache.ambari.server.api.resources.*;
import org.apache.ambari.server.api.services.parsers.JsonPropertyParser;
import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
import org.apache.ambari.server.api.services.serializers.JsonSerializer;
import org.apache.ambari.server.api.services.serializers.ResultSerializer;
import org.apache.ambari.server.controller.internal.TemporalInfoImpl;
import org.apache.ambari.server.controller.predicate.*;
import org.apache.ambari.server.controller.spi.Predicate;
import org.apache.ambari.server.controller.spi.TemporalInfo;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriInfo;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Request implementation.
*/
public abstract class BaseRequest implements Request {
/**
* URI information
*/
private UriInfo m_uriInfo;
/**
* Http headers
*/
private HttpHeaders m_headers;
/**
* Http Body
*/
private String m_body;
/**
* Predicate operators.
*/
private Pattern m_pattern = Pattern.compile("!=|>=|<=|=|>|<");
/**
* Associated resource definition
*/
private ResourceInstance m_resource;
/**
* Constructor.
*
* @param headers http headers
* @param body http body
* @param uriInfo uri information
* @param resource associated resource definition
*/
public BaseRequest(HttpHeaders headers, String body, UriInfo uriInfo, ResourceInstance resource) {
m_headers = headers;
m_body = body;
m_uriInfo = uriInfo;
m_resource = resource;
}
@Override
public ResourceInstance getResource() {
return m_resource;
}
@Override
public String getURI() {
try {
return URLDecoder.decode(m_uriInfo.getRequestUri().toASCIIString(), "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unable to decode URI: " + e, e);
}
}
@Override
public int getAPIVersion() {
return 0;
}
@Override
public Predicate getQueryPredicate() {
//todo: parse during init
//not using getQueryParameters because it assumes '=' operator
String uri = getURI();
int qsBegin = uri.indexOf("?");
if (qsBegin == -1) return null;
String[] tokens = uri.substring(qsBegin + 1).split("&");
Set<BasePredicate> setPredicates = new HashSet<BasePredicate>();
for (String outerToken : tokens) {
if (outerToken.startsWith("_=")) {
// NOTE: This is to enable UI to pass a _= parameter for unique query
// string even though the backend doesnt need it.
continue;
}
if (outerToken != null && !outerToken.startsWith("fields")) {
setPredicates.add(outerToken.contains("|") ?
handleOrPredicate(outerToken) : createPredicate(outerToken));
}
}
if (setPredicates.size() == 1) {
return setPredicates.iterator().next();
} else if (setPredicates.size() > 1) {
return new AndPredicate(setPredicates.toArray(new BasePredicate[setPredicates.size()]));
} else {
return null;
}
}
@Override
public Map<String, TemporalInfo> getFields() {
Map<String, TemporalInfo> mapProperties;
String partialResponseFields = m_uriInfo.getQueryParameters().getFirst("fields");
if (partialResponseFields == null) {
mapProperties = Collections.emptyMap();
} else {
Set<String> setMatches = new HashSet<String>();
// Pattern basically splits a string using ',' as the deliminator unless ',' is between '[' and ']'.
// Actually, captures char sequences between ',' and all chars between '[' and ']' including ','.
Pattern re = Pattern.compile("[^,\\[]*?\\[[^\\]]*?\\]|[^,]+");
Matcher m = re.matcher(partialResponseFields);
while (m.find()){
for (int groupIdx = 0; groupIdx < m.groupCount() + 1; groupIdx++) {
setMatches.add(m.group(groupIdx));
}
}
mapProperties = new HashMap<String, TemporalInfo>(setMatches.size());
for (String field : setMatches) {
TemporalInfo temporalInfo = null;
if (field.contains("[")) {
String[] temporalData = field.substring(field.indexOf('[') + 1,
field.indexOf(']')).split(",");
field = field.substring(0, field.indexOf('['));
long start = Long.parseLong(temporalData[0].trim());
long end = -1;
long step = -1;
if (temporalData.length >= 2) {
end = Long.parseLong(temporalData[1].trim());
if (temporalData.length == 3) {
step = Long.parseLong(temporalData[2].trim());
}
}
temporalInfo = new TemporalInfoImpl(start, end, step);
}
mapProperties.put(field, temporalInfo);
}
}
return mapProperties;
}
@Override
public Map<String, List<String>> getHttpHeaders() {
return m_headers.getRequestHeaders();
}
@Override
public String getHttpBody() {
return m_body;
}
@Override
public Set<Map<String, Object>> getHttpBodyProperties() {
return getHttpBodyParser().parse(getHttpBody());
}
@Override
public ResultSerializer getResultSerializer() {
return new JsonSerializer();
}
@Override
public ResultPostProcessor getResultPostProcessor() {
return new ResultPostProcessorImpl(this);
}
private BasePredicate createPredicate(String token) {
Matcher m = m_pattern.matcher(token);
m.find();
String propertyId = token.substring(0, m.start());
String value = token.substring(m.end());
String operator = m.group();
if (operator.equals("=")) {
return new EqualsPredicate<String>(propertyId, value);
} else if (operator.equals("!=")) {
return new NotPredicate(new EqualsPredicate<String>(propertyId, value));
} else if (operator.equals("<")) {
return new LessPredicate<String>(propertyId, value);
} else if (operator.equals(">")) {
return new GreaterPredicate<String>(propertyId, value);
} else if (operator.equals("<=")) {
return new LessEqualsPredicate<String>(propertyId, value);
} else if (operator.equals(">=")) {
return new GreaterEqualsPredicate<String>(propertyId, value);
} else {
throw new RuntimeException("Unknown operator provided in predicate: " + operator);
}
}
private BasePredicate handleOrPredicate(String predicate) {
Set<BasePredicate> setPredicates = new HashSet<BasePredicate>();
String[] tokens = predicate.split("\\|");
for (String tok : tokens) {
setPredicates.add(createPredicate(tok));
}
return new OrPredicate(setPredicates.toArray(new BasePredicate[setPredicates.size()]));
}
protected RequestBodyParser getHttpBodyParser() {
return new JsonPropertyParser();
}
}