| /* |
| * 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); |
| } |
| } |