| /* |
| * 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.util.LinkedList; |
| import java.util.List; |
| |
| import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; |
| import org.apache.olingo.commons.api.ex.ODataRuntimeException; |
| import org.apache.olingo.commons.api.format.ContentType; |
| import org.apache.olingo.commons.api.http.HttpHeader; |
| import org.apache.olingo.commons.api.http.HttpMethod; |
| import org.apache.olingo.server.api.OData; |
| import org.apache.olingo.server.api.ODataApplicationException; |
| import org.apache.olingo.server.api.ODataHandler; |
| 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.ODataServerError; |
| import org.apache.olingo.server.api.OlingoExtension; |
| import org.apache.olingo.server.api.ServiceMetadata; |
| import org.apache.olingo.server.api.deserializer.DeserializerException; |
| import org.apache.olingo.server.api.etag.CustomETagSupport; |
| import org.apache.olingo.server.api.etag.PreconditionException; |
| import org.apache.olingo.server.api.processor.DefaultProcessor; |
| import org.apache.olingo.server.api.processor.ErrorProcessor; |
| import org.apache.olingo.server.api.processor.Processor; |
| import org.apache.olingo.server.api.serializer.CustomContentTypeSupport; |
| import org.apache.olingo.server.api.serializer.RepresentationType; |
| import org.apache.olingo.server.api.serializer.SerializerException; |
| import org.apache.olingo.server.api.uri.UriInfo; |
| import org.apache.olingo.server.api.uri.queryoption.FormatOption; |
| import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind; |
| import org.apache.olingo.server.core.debug.ServerCoreDebugger; |
| import org.apache.olingo.server.core.uri.parser.Parser; |
| import org.apache.olingo.server.core.uri.parser.UriParserException; |
| import org.apache.olingo.server.core.uri.parser.UriParserSemanticException; |
| import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException; |
| import org.apache.olingo.server.core.uri.queryoption.FormatOptionImpl; |
| import org.apache.olingo.server.core.uri.validator.UriValidationException; |
| import org.apache.olingo.server.core.uri.validator.UriValidator; |
| |
| public class ODataHandlerImpl implements ODataHandler { |
| |
| private final OData odata; |
| private final ServiceMetadata serviceMetadata; |
| private final List<Processor> processors = new LinkedList<>(); |
| private final ServerCoreDebugger debugger; |
| |
| private CustomContentTypeSupport customContentTypeSupport; |
| private CustomETagSupport customETagSupport; |
| |
| private UriInfo uriInfo; |
| private Exception lastThrownException; |
| |
| public ODataHandlerImpl(final OData odata, final ServiceMetadata serviceMetadata, final ServerCoreDebugger debugger) { |
| this.odata = odata; |
| this.serviceMetadata = serviceMetadata; |
| this.debugger = debugger; |
| |
| register(new DefaultRedirectProcessor()); |
| register(new DefaultProcessor()); |
| } |
| |
| public ODataResponse process(final ODataRequest request) { |
| ODataResponse response = new ODataResponse(); |
| final int responseHandle = debugger.startRuntimeMeasurement("ODataHandler", "process"); |
| try { |
| processInternal(request, response); |
| } catch (final UriValidationException e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); |
| handleException(request, response, serverError, e); |
| } catch (final UriParserSemanticException e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); |
| handleException(request, response, serverError, e); |
| } catch (final UriParserSyntaxException e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); |
| handleException(request, response, serverError, e); |
| } catch (final UriParserException e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); |
| handleException(request, response, serverError, e); |
| } catch (AcceptHeaderContentNegotiatorException e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); |
| handleException(request, response, serverError, e); |
| } catch (ContentNegotiatorException e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); |
| handleException(request, response, serverError, e); |
| } catch (SerializerException e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); |
| handleException(request, response, serverError, e); |
| } catch (DeserializerException e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); |
| handleException(request, response, serverError, e); |
| } catch (PreconditionException e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); |
| handleException(request, response, serverError, e); |
| } catch (ODataHandlerException e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); |
| handleException(request, response, serverError, e); |
| } catch (ODataApplicationException e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e); |
| handleException(request, response, serverError, e); |
| } catch (Exception e) { |
| ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e); |
| handleException(request, response, serverError, e); |
| } |
| debugger.stopRuntimeMeasurement(responseHandle); |
| return response; |
| } |
| |
| private void processInternal(final ODataRequest request, final ODataResponse response) |
| throws ODataApplicationException, ODataLibraryException { |
| final int measurementHandle = debugger.startRuntimeMeasurement("ODataHandler", "processInternal"); |
| |
| response.setHeader(HttpHeader.ODATA_VERSION, ODataServiceVersion.V40.toString()); |
| |
| try { |
| validateODataVersion(request); |
| } catch (final ODataHandlerException e) { |
| debugger.stopRuntimeMeasurement(measurementHandle); |
| throw e; |
| } |
| |
| final int measurementUriParser = debugger.startRuntimeMeasurement("Parser", "parseUri"); |
| try { |
| uriInfo = new Parser(serviceMetadata.getEdm(), odata) |
| .parseUri(request.getRawODataPath(), request.getRawQueryPath(), null, request.getRawBaseUri()); |
| } catch (final ODataLibraryException e) { |
| debugger.stopRuntimeMeasurement(measurementUriParser); |
| debugger.stopRuntimeMeasurement(measurementHandle); |
| throw e; |
| } |
| debugger.stopRuntimeMeasurement(measurementUriParser); |
| |
| final int measurementUriValidator = debugger.startRuntimeMeasurement("UriValidator", "validate"); |
| final HttpMethod method = request.getMethod(); |
| try { |
| new UriValidator().validate(uriInfo, method); |
| } catch (final UriValidationException e) { |
| debugger.stopRuntimeMeasurement(measurementUriValidator); |
| debugger.stopRuntimeMeasurement(measurementHandle); |
| throw e; |
| } |
| debugger.stopRuntimeMeasurement(measurementUriValidator); |
| |
| final int measurementDispatcher = debugger.startRuntimeMeasurement("ODataDispatcher", "dispatch"); |
| try { |
| new ODataDispatcher(uriInfo, this).dispatch(request, response); |
| } finally { |
| debugger.stopRuntimeMeasurement(measurementDispatcher); |
| debugger.stopRuntimeMeasurement(measurementHandle); |
| } |
| } |
| |
| public void handleException(final ODataRequest request, final ODataResponse response, |
| final ODataServerError serverError, final Exception exception) { |
| final int measurementHandle = debugger.startRuntimeMeasurement("ODataHandler", "handleException"); |
| lastThrownException = exception; |
| ErrorProcessor exceptionProcessor; |
| try { |
| exceptionProcessor = selectProcessor(ErrorProcessor.class); |
| } catch (ODataHandlerException e) { |
| // This cannot happen since there is always an ExceptionProcessor registered. |
| exceptionProcessor = new DefaultProcessor(); |
| } |
| ContentType requestedContentType; |
| try { |
| final FormatOption formatOption = getFormatOption(request, uriInfo); |
| requestedContentType = ContentNegotiator.doContentNegotiation(formatOption, request, |
| getCustomContentTypeSupport(), RepresentationType.ERROR); |
| } catch (final AcceptHeaderContentNegotiatorException e) { |
| requestedContentType = ContentType.JSON; |
| } catch (final ContentNegotiatorException e) { |
| requestedContentType = ContentType.JSON; |
| } |
| final int measurementError = debugger.startRuntimeMeasurement("ErrorProcessor", "processError"); |
| exceptionProcessor.processError(request, response, serverError, requestedContentType); |
| debugger.stopRuntimeMeasurement(measurementError); |
| debugger.stopRuntimeMeasurement(measurementHandle); |
| } |
| |
| /** |
| * Extract format option from either <code>uriInfo</code> (if not <code>NULL</code>) |
| * or query from <code>request</code> (if not <code>NULL</code>). |
| * If both options are <code>NULL</code>, <code>NULL</code> is returned. |
| * |
| * @param request request which is checked |
| * @param uriInfo uriInfo which is checked |
| * @return the evaluated format option or <code>NULL</code>. |
| */ |
| private FormatOption getFormatOption(final ODataRequest request, final UriInfo uriInfo) { |
| if(uriInfo == null) { |
| String query = request.getRawQueryPath(); |
| if(query == null) { |
| return null; |
| } |
| |
| final String formatOption = SystemQueryOptionKind.FORMAT.toString(); |
| int index = query.indexOf(formatOption); |
| int endIndex = query.indexOf('&', index); |
| if(endIndex == -1) { |
| endIndex = query.length(); |
| } |
| String format = ""; |
| if (index + formatOption.length() < endIndex) { |
| format = query.substring(index + formatOption.length(), endIndex); |
| } |
| return new FormatOptionImpl().setFormat(format); |
| } |
| return uriInfo.getFormatOption(); |
| } |
| |
| private void validateODataVersion(final ODataRequest request) throws ODataHandlerException { |
| final String odataVersion = request.getHeader(HttpHeader.ODATA_VERSION); |
| if (odataVersion != null && !ODataServiceVersion.isValidODataVersion(odataVersion)) { |
| throw new ODataHandlerException("ODataVersion not supported: " + odataVersion, |
| ODataHandlerException.MessageKeys.ODATA_VERSION_NOT_SUPPORTED, odataVersion); |
| } |
| |
| final String maxVersion = request.getHeader(HttpHeader.ODATA_MAX_VERSION); |
| if (maxVersion != null && !ODataServiceVersion.isValidMaxODataVersion(maxVersion)) { |
| throw new ODataHandlerException("ODataVersion not supported: " + maxVersion, |
| ODataHandlerException.MessageKeys.ODATA_VERSION_NOT_SUPPORTED, maxVersion); |
| } |
| } |
| |
| <T extends Processor> T selectProcessor(final Class<T> cls) throws ODataHandlerException { |
| for (final Processor processor : processors) { |
| if (cls.isAssignableFrom(processor.getClass())) { |
| processor.init(odata, serviceMetadata); |
| return cls.cast(processor); |
| } |
| } |
| throw new ODataHandlerException("Processor: " + cls.getSimpleName() + " not registered.", |
| ODataHandlerException.MessageKeys.PROCESSOR_NOT_IMPLEMENTED, cls.getSimpleName()); |
| } |
| |
| public void register(final Processor processor) { |
| processors.add(0, processor); |
| } |
| |
| @Override |
| public void register(OlingoExtension extension) { |
| if(extension instanceof CustomContentTypeSupport) { |
| this.customContentTypeSupport = (CustomContentTypeSupport) extension; |
| } else if(extension instanceof CustomETagSupport) { |
| this.customETagSupport = (CustomETagSupport) extension; |
| } else { |
| throw new ODataRuntimeException("Got not supported exception with class name " + |
| extension.getClass().getSimpleName()); |
| } |
| } |
| |
| public CustomContentTypeSupport getCustomContentTypeSupport() { |
| return customContentTypeSupport; |
| } |
| |
| public CustomETagSupport getCustomETagSupport() { |
| return customETagSupport; |
| } |
| |
| public Exception getLastThrownException() { |
| return lastThrownException; |
| } |
| |
| public UriInfo getUriInfo() { |
| return uriInfo; |
| } |
| } |