blob: 03dba3ea56b96d02f811adc0644a75696b037096 [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.handlers.RequestHandler;
import org.apache.ambari.server.api.predicate.InvalidQueryException;
import org.apache.ambari.server.api.predicate.PredicateCompiler;
import org.apache.ambari.server.api.resources.*;
import org.apache.ambari.server.controller.internal.PageRequestImpl;
import org.apache.ambari.server.controller.internal.TemporalInfoImpl;
import org.apache.ambari.server.controller.spi.PageRequest;
import org.apache.ambari.server.controller.spi.Predicate;
import org.apache.ambari.server.controller.spi.TemporalInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 RequestBody m_body;
/**
* Query Predicate
*/
private Predicate m_predicate;
/**
* Associated resource definition
*/
private ResourceInstance m_resource;
/**
* Default page size for pagination request.
*/
private static final int DEFAULT_PAGE_SIZE = 20;
/**
* Logger instance.
*/
private final static Logger LOG = LoggerFactory.getLogger(Request.class);
/**
* Constructor.
*
* @param headers http headers
* @param body http body
* @param uriInfo uri information
* @param resource associated resource definition
*
*/
public BaseRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, ResourceInstance resource) {
m_headers = headers;
m_uriInfo = uriInfo;
m_resource = resource;
m_body = body;
}
@Override
public Result process() {
if (LOG.isDebugEnabled()) {
LOG.debug("Handling API Request: '" + getURI() + "'");
}
Result result;
try {
parseQueryPredicate();
result = getRequestHandler().handleRequest(this);
} catch (InvalidQueryException e) {
result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST,
"Unable to compile query predicate: " + e.getMessage()));
}
if (! result.getStatus().isErrorState()) {
getResultPostProcessor().process(result);
}
return result;
}
@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 1;
}
@Override
public Predicate getQueryPredicate() {
return m_predicate;
}
@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 PageRequest getPageRequest() {
String pageSize = m_uriInfo.getQueryParameters().getFirst("page_size");
String from = m_uriInfo.getQueryParameters().getFirst("from");
String to = m_uriInfo.getQueryParameters().getFirst("to");
if (pageSize == null && from == null && to == null) {
return null;
}
int offset = 0;
PageRequest.StartingPoint startingPoint;
// TODO : support other starting points
if (from != null) {
if(from.equals("start")) {
startingPoint = PageRequest.StartingPoint.Beginning;
} else {
offset = Integer.valueOf(from);
startingPoint = PageRequest.StartingPoint.OffsetStart;
}
} else if (to != null ) {
if (to.equals("end")) {
startingPoint = PageRequest.StartingPoint.End;
} else {
offset = Integer.valueOf(to);
startingPoint = PageRequest.StartingPoint.OffsetEnd;
}
} else {
startingPoint = PageRequest.StartingPoint.Beginning;
}
// TODO : support predicate and comparator
return new PageRequestImpl(startingPoint,
pageSize == null ? DEFAULT_PAGE_SIZE : Integer.valueOf(pageSize), offset, null, null);
}
@Override
public RequestBody getBody() {
return m_body;
}
/**
* Obtain the result post processor for the request.
*
* @return the result post processor
*/
protected ResultPostProcessor getResultPostProcessor() {
//todo: inject
return new ResultPostProcessorImpl(this);
}
/**
* Obtain the predicate compiler which is used to compile the query string into
* a predicate.
*
* @return the predicate compiler
*/
protected PredicateCompiler getPredicateCompiler() {
return new PredicateCompiler();
}
/**
* Parse the query string and compile it into a predicate.
* The query string may have already been extracted from the http body.
* If the query string didn't exist in the body use the query string in the URL.
*
* @throws InvalidQueryException if unable to parse a non-null query string into a predicate
*/
private void parseQueryPredicate() throws InvalidQueryException {
String queryString = m_body.getQueryString();
if (queryString == null) {
String uri = getURI();
int qsBegin = uri.indexOf("?");
queryString = (qsBegin == -1) ? null : uri.substring(qsBegin + 1);
}
if (queryString != null) {
m_predicate = getPredicateCompiler().compile(queryString);
}
}
/**
* Obtain the underlying request handler for the request.
*
* @return the request handler
*/
protected abstract RequestHandler getRequestHandler();
}