blob: e704fddf814183d0daa4fbd735dc109effe4c4dd [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.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;
}
}