blob: bb311d3b0628fcdef6fafc159e80adeaac068af3 [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.olingo.server.core;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.StringTokenizer;
import org.apache.olingo.commons.api.ex.ODataException;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.commons.core.Decoder;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.ODataLibraryException;
import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.ODataResponse;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
import org.apache.olingo.server.api.serializer.RepresentationType;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.UriInfoBatch;
import org.apache.olingo.server.api.uri.UriInfoCrossjoin;
import org.apache.olingo.server.api.uri.UriInfoEntityId;
import org.apache.olingo.server.api.uri.UriInfoMetadata;
import org.apache.olingo.server.api.uri.UriInfoService;
import org.apache.olingo.server.api.uri.UriResourceAction;
import org.apache.olingo.server.api.uri.UriResourceComplexProperty;
import org.apache.olingo.server.api.uri.UriResourceCount;
import org.apache.olingo.server.api.uri.UriResourceEntitySet;
import org.apache.olingo.server.api.uri.UriResourceFunction;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty;
import org.apache.olingo.server.api.uri.UriResourceRef;
import org.apache.olingo.server.api.uri.UriResourceSingleton;
import org.apache.olingo.server.api.uri.UriResourceValue;
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
import org.apache.olingo.server.core.requests.ActionRequest;
import org.apache.olingo.server.core.requests.BatchRequest;
import org.apache.olingo.server.core.requests.DataRequest;
import org.apache.olingo.server.core.requests.FunctionRequest;
import org.apache.olingo.server.core.requests.MediaRequest;
import org.apache.olingo.server.core.requests.MetadataRequest;
import org.apache.olingo.server.core.requests.OperationRequest;
import org.apache.olingo.server.core.requests.ServiceDocumentRequest;
import org.apache.olingo.server.core.uri.parser.Parser;
import org.apache.olingo.server.core.uri.validator.UriValidator;
public class ServiceDispatcher extends RequestURLHierarchyVisitor {
private final OData odata;
protected ServiceMetadata metadata;
protected ServiceHandler handler;
protected CustomContentTypeSupport customContentSupport;
private String idOption;
protected ServiceRequest request;
public ServiceDispatcher(OData odata, ServiceMetadata metadata, ServiceHandler handler,
CustomContentTypeSupport customContentSupport) {
this.odata = odata;
this.metadata = metadata;
this.handler = handler;
this.customContentSupport = customContentSupport;
}
public void execute(ODataRequest odRequest, ODataResponse odResponse) {
FormatOption formatOption = null;
ODataException oDataException = null;
try {
String path = odRequest.getRawODataPath();
String query = odRequest.getRawQueryPath();
if(path.indexOf("$entity") != -1) {
executeIdOption(query, odRequest, odResponse);
} else {
UriInfo uriInfo = new Parser(this.metadata.getEdm(), odata)
.parseUri(path, query, null, odRequest.getRawBaseUri());
formatOption = uriInfo.getFormatOption();
internalExecute(uriInfo, odRequest, odResponse);
}
return;
} catch(ODataLibraryException e) {
oDataException = e;
} catch(ODataApplicationException e) {
oDataException = e;
}
ContentType contentType = ContentType.JSON;
try {
contentType = ContentNegotiator.doContentNegotiation(formatOption,
odRequest, this.customContentSupport, RepresentationType.ERROR);
} catch (ContentNegotiatorException e) {
// ignore, default to JSON
}
handleException(oDataException, contentType, odRequest, odResponse);
}
protected void handleException(ODataException e, ContentType contentType,
ODataRequest odRequest, ODataResponse odResponse) {
ErrorHandler errorHandler = new ErrorHandler(this.odata, this.metadata,
this.handler, contentType);
errorHandler.handleException(e, odRequest, odResponse);
}
private void internalExecute(UriInfo uriInfo, ODataRequest odRequest,
ODataResponse odResponse) throws ODataLibraryException,
ODataApplicationException {
new UriValidator().validate(uriInfo, odRequest.getMethod());
// part1, 8.2.6
String isolation = odRequest.getHeader(HttpHeader.ODATA_ISOLATION);
if (isolation != null && "snapshot".equals(isolation) && !this.handler.supportsDataIsolation()) {
odResponse.setStatusCode(HttpStatusCode.PRECONDITION_FAILED.getStatusCode());
return;
}
visit(uriInfo);
// this should cover for any unsupported calls until they are implemented
if (this.request == null) {
this.request = new ServiceRequest(this.odata, this.metadata) {
@Override
public ContentType getResponseContentType() throws ContentNegotiatorException {
return ContentType.APPLICATION_JSON;
}
@Override
public void execute(ServiceHandler handler, ODataResponse response)
throws ODataLibraryException, ODataApplicationException {
handler.anyUnsupported(getODataRequest(), response);
}
};
}
// To handle $entity?$id=http://localhost/EntitySet(key) as
// http://localhost/EntitySet(key)
if (this.idOption != null) {
try {
this.request.setODataRequest(odRequest);
this.request = this.request.parseLink(new URI(this.idOption));
} catch (URISyntaxException e) {
throw new ODataHandlerException("Invalid $id value",
ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED, this.idOption);
}
}
this.request.setODataRequest(odRequest);
this.request.setUriInfo(uriInfo);
this.request.setCustomContentTypeSupport(this.customContentSupport);
this.request.execute(this.handler, odResponse);
}
@Override
public void visit(UriInfoMetadata info) {
this.request = new MetadataRequest(this.odata, this.metadata);
}
@Override
public void visit(UriInfoService info) {
this.request = new ServiceDocumentRequest(this.odata, this.metadata);
}
@Override
public void visit(UriResourceEntitySet info) {
DataRequest dataRequest = new DataRequest(this.odata, this.metadata);
dataRequest.setUriResourceEntitySet(info);
this.request = dataRequest;
}
@Override
public void visit(UriResourceCount option) {
if (this.request instanceof DataRequest) {
DataRequest dataRequest = (DataRequest) this.request;
dataRequest.setCountRequest(option != null);
} else if (this.request instanceof OperationRequest) {
OperationRequest opRequest = (OperationRequest) this.request;
opRequest.setCountRequest(option != null);
}
}
@Override
public void visit(UriResourceComplexProperty info) {
DataRequest dataRequest = (DataRequest) this.request;
dataRequest.setUriResourceProperty(info);
}
@Override
public void visit(UriResourcePrimitiveProperty info) {
DataRequest dataRequest = (DataRequest) this.request;
dataRequest.setUriResourceProperty(info);
}
@Override
public void visit(UriResourceValue info) {
DataRequest dataRequest = (DataRequest) this.request;
if (dataRequest.isPropertyRequest()) {
dataRequest.setValueRequest(info != null);
} else {
MediaRequest mediaRequest = new MediaRequest(this.odata, this.metadata);
mediaRequest.setUriResourceEntitySet(dataRequest.getUriResourceEntitySet());
this.request = mediaRequest;
}
}
@Override
public void visit(UriResourceAction info) {
ActionRequest actionRequest = new ActionRequest(this.odata, this.metadata);
actionRequest.setUriResourceAction(info);
this.request = actionRequest;
}
@Override
public void visit(UriResourceFunction info) {
FunctionRequest functionRequest = new FunctionRequest(this.odata, this.metadata);
functionRequest.setUriResourceFunction(info);
this.request = functionRequest;
}
@Override
public void visit(UriResourceNavigation info) {
DataRequest dataRequest = (DataRequest) this.request;
dataRequest.addUriResourceNavigation(info);
}
@Override
public void visit(UriResourceRef info) {
// this is same as data, but return is just entity references.
DataRequest dataRequest = (DataRequest) this.request;
dataRequest.setReferenceRequest(info != null);
}
@Override
public void visit(UriInfoBatch info) {
this.request = new BatchRequest(this.odata, this.metadata);
}
@Override
public void visit(UriResourceSingleton info) {
DataRequest dataRequest = new DataRequest(this.odata, this.metadata);
dataRequest.setUriResourceSingleton(info);
this.request = dataRequest;
}
@Override
public void visit(UriInfoEntityId info) {
DataRequest dataRequest = new DataRequest(this.odata, this.metadata);
this.request = dataRequest;
super.visit(info);
}
@Override
public void visit(UriInfoCrossjoin info) {
DataRequest dataRequest = new DataRequest(this.odata, this.metadata);
dataRequest.setCrossJoin(info);
this.request = dataRequest;
}
@Override
public void visit(ApplyOption option) {
((DataRequest)this.request).setApply(option);
}
private void executeIdOption(String query, ODataRequest odRequest,
ODataResponse odResponse) throws ODataLibraryException,
ODataApplicationException {
StringBuilder sb = new StringBuilder();
StringTokenizer st = new StringTokenizer(query, "&");
boolean first = true;
while(st.hasMoreTokens()) {
String token = st.nextToken();
if (token.startsWith("$id=")) {
URI id = URI.create(Decoder.decode(token.substring(4)));
sb.append(id.getPath());
} else {
if (first) {
sb.append("?");
} else {
sb.append("&");
}
sb.append(token);
}
}
DataRequest dataRequest = new DataRequest(this.odata, this.metadata);
this.request = dataRequest;
this.request.setODataRequest(odRequest);
this.request = this.request.parseLink(URI.create(sb.toString()));
this.request.setODataRequest(odRequest);
this.request.setCustomContentTypeSupport(this.customContentSupport);
this.request.execute(this.handler, odResponse);
}
}