package ca.uhn.fhir.jaxrs.server;
import java.io.IOException;
/*
* #%L
* HAPI FHIR JAX-RS Server
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed 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.
* #L%
*/
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
import ca.uhn.fhir.rest.server.IServerAddressStrategy;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.util.OperationOutcomeUtil;
/**
* This is the abstract superclass for all jaxrs providers. It contains some defaults implementing
* the IRestfulServerDefaults interface and exposes the uri and headers.
*
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
private final FhirContext CTX;
private static final String PROCESSING = "processing";
private static final String ERROR = "error";
/** the uri info */
@Context
private UriInfo theUriInfo;
/** the http headers */
@Context
private HttpHeaders theHeaders;
@Override
public FhirContext getFhirContext() {
return CTX;
}
/**
* Default is DSTU2. Use {@link AbstractJaxRsProvider#AbstractJaxRsProvider(FhirContext)} to specify a DSTU3 context.
*/
protected AbstractJaxRsProvider() {
CTX = FhirContext.forDstu2();
}
/**
*
* @param ctx the {@link FhirContext} to support.
*/
protected AbstractJaxRsProvider(final FhirContext ctx) {
CTX = ctx;
}
/**
* This method returns the query parameters
* @return the query parameters
*/
public Map<String, String[]> getParameters() {
final MultivaluedMap<String, String> queryParameters = getUriInfo().getQueryParameters();
final HashMap<String, String[]> params = new HashMap<String, String[]>();
for (final Entry<String, List<String>> paramEntry : queryParameters.entrySet()) {
params.put(paramEntry.getKey(), paramEntry.getValue().toArray(new String[paramEntry.getValue().size()]));
}
return params;
}
/**
* This method returns the default server address strategy. The default strategy return the
* base uri for the request {@link AbstractJaxRsProvider#getBaseForRequest() getBaseForRequest()}
* @return
*/
public IServerAddressStrategy getServerAddressStrategy() {
final HardcodedServerAddressStrategy addressStrategy = new HardcodedServerAddressStrategy();
addressStrategy.setValue(getBaseForRequest());
return addressStrategy;
}
/**
* This method returns the server base, independent of the request or resource.
* @see javax.ws.rs.core.UriInfo#getBaseUri()
* @return the ascii string for the server base
*/
public String getBaseForServer() {
final String url = getUriInfo().getBaseUri().toASCIIString();
return StringUtils.isNotBlank(url) && url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
}
/**
* This method returns the server base, including the resource path.
* {@link javax.ws.rs.core.UriInfo#getBaseUri() UriInfo#getBaseUri()}
* @return the ascii string for the base resource provider path
*/
public String getBaseForRequest() {
return getBaseForServer();
}
/**
* Default: an empty list of interceptors (Interceptors are not yet supported
* in the JAX-RS server). Please get in touch if you'd like to help!
*
* @see ca.uhn.fhir.rest.server.IRestfulServer#getInterceptors()
*/
@Override
public List<IServerInterceptor> getInterceptors() {
return Collections.emptyList();
}
/**
* Get the uriInfo
* @return the uri info
*/
public UriInfo getUriInfo() {
return this.theUriInfo;
}
/**
* Set the Uri Info
* @param uriInfo the uri info
*/
public void setUriInfo(final UriInfo uriInfo) {
this.theUriInfo = uriInfo;
}
/**
* Get the headers
* @return the headers
*/
public HttpHeaders getHeaders() {
return this.theHeaders;
}
/**
* Set the headers
* @param headers the headers to set
*/
public void setHeaders(final HttpHeaders headers) {
this.theHeaders = headers;
}
/**
* Return the requestbuilder for the server
* @param requestType the type of the request
* @param restOperation the rest operation type
* @param theResourceName the resource name
* @return the requestbuilder
*/
public Builder getRequest(final RequestTypeEnum requestType, final RestOperationTypeEnum restOperation, final String theResourceName) {
return new JaxRsRequest.Builder(this, requestType, restOperation, theUriInfo.getRequestUri().toString(), theResourceName);
}
/**
* Return the requestbuilder for the server
* @param requestType the type of the request
* @param restOperation the rest operation type
* @return the requestbuilder
*/
public Builder getRequest(final RequestTypeEnum requestType, final RestOperationTypeEnum restOperation) {
return getRequest(requestType, restOperation, null);
}
/**
* DEFAULT = EncodingEnum.JSON
*/
@Override
public EncodingEnum getDefaultResponseEncoding() {
return EncodingEnum.JSON;
}
/**
* DEFAULT = true
*/
@Override
public boolean isDefaultPrettyPrint() {
return true;
}
/**
* DEFAULT = ETagSupportEnum.DISABLED
*/
@Override
public ETagSupportEnum getETagSupport() {
return ETagSupportEnum.DISABLED;
}
/**
* DEFAULT = AddProfileTagEnum.NEVER
*/
@Override
public AddProfileTagEnum getAddProfileTag() {
return AddProfileTagEnum.NEVER;
}
/**
* DEFAULT = false
*/
@Override
public boolean isUseBrowserFriendlyContentTypes() {
return true;
}
/**
* DEFAULT = false
*/
public boolean withStackTrace() {
return false;
}
/**
* Convert an exception to a response
* @param theRequest the incoming request
* @param theException the exception to convert
* @return response
* @throws IOException
*/
public Response handleException(final JaxRsRequest theRequest, final Throwable theException)
throws IOException {
if (theException instanceof JaxRsResponseException) {
return new JaxRsExceptionInterceptor().convertExceptionIntoResponse(theRequest, (JaxRsResponseException) theException);
} else if (theException instanceof DataFormatException) {
return new JaxRsExceptionInterceptor().convertExceptionIntoResponse(theRequest, new JaxRsResponseException(
new InvalidRequestException(theException.getMessage(), createOutcome((DataFormatException) theException))));
} else {
return new JaxRsExceptionInterceptor().convertExceptionIntoResponse(theRequest,
new JaxRsExceptionInterceptor().convertException(this, theException));
}
}
private IBaseOperationOutcome createOutcome(final DataFormatException theException) {
final IBaseOperationOutcome oo = OperationOutcomeUtil.newInstance(getFhirContext());
final String detailsValue = theException.getMessage() + "\n\n" + ExceptionUtils.getStackTrace(theException);
OperationOutcomeUtil.addIssue(getFhirContext(), oo, ERROR, detailsValue, null, PROCESSING);
return oo;
}
}