diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index ee4b0af5013..b9e398b4a56 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server; * 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. @@ -19,42 +19,62 @@ package ca.uhn.fhir.rest.server; * limitations under the License. * #L% */ -import static org.apache.commons.lang3.StringUtils.isBlank; -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import java.io.*; -import java.lang.annotation.Annotation; -import java.lang.reflect.*; -import java.util.*; -import java.util.Map.Entry; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.jar.Manifest; - -import javax.servlet.ServletException; -import javax.servlet.UnavailableException; -import javax.servlet.http.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.ProvidedResourceScanner; +import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.context.api.AddProfileTagEnum; +import ca.uhn.fhir.context.api.BundleInclusionRule; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.rest.annotation.Destroy; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Initialize; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.server.IFhirVersionServer; +import ca.uhn.fhir.rest.api.server.IRestfulServer; +import ca.uhn.fhir.rest.api.server.ParseAction; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding; +import ca.uhn.fhir.rest.server.exceptions.*; +import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; +import ca.uhn.fhir.rest.server.method.BaseMethodBinding; +import ca.uhn.fhir.rest.server.method.ConformanceMethodBinding; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.tenant.ITenantIdentificationStrategy; +import ca.uhn.fhir.util.*; +import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import ca.uhn.fhir.context.*; -import ca.uhn.fhir.context.api.AddProfileTagEnum; -import ca.uhn.fhir.context.api.BundleInclusionRule; -import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.api.server.*; -import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding; -import ca.uhn.fhir.rest.server.exceptions.*; -import ca.uhn.fhir.rest.server.interceptor.*; -import ca.uhn.fhir.rest.server.method.*; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import ca.uhn.fhir.util.*; +import javax.servlet.ServletException; +import javax.servlet.UnavailableException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.Writer; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.jar.Manifest; + +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; @SuppressWarnings("WeakerAccess") public class RestfulServer extends HttpServlet implements IRestfulServer { @@ -96,7 +116,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer myServerConformanceMethod; private Object myServerConformanceProvider; private String myServerName = "HAPI FHIR Server"; - /** This is configurable but by default we just use HAPI version */ + /** + * This is configurable but by default we just use HAPI version + */ private String myServerVersion = VersionUtil.getVersion(); private boolean myStarted; private Map myTypeToProvider = new HashMap<>(); @@ -121,10 +143,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer 0 && (nextString.charAt(0) == '_' || nextString.charAt(0) == '$' || nextString.equals(Constants.URL_TOKEN_METADATA)); - } - private void addContentLocationHeaders(RequestDetails theRequest, HttpServletResponse servletResponse, MethodOutcome response, String resourceName) { if (response != null && response.getId() != null) { addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_LOCATION, resourceName); @@ -140,15 +158,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer */ public void addHeadersToResponse(HttpServletResponse theHttpResponse) { - StringBuilder b = new StringBuilder(); - b.append("HAPI FHIR "); - b.append(VersionUtil.getVersion()); - b.append(" REST Server (FHIR Server; FHIR "); - b.append(myFhirContext.getVersion().getVersion().getFhirVersionString()); - b.append('/'); - b.append(myFhirContext.getVersion().getVersion().name()); - b.append(")"); - theHttpResponse.addHeader("X-Powered-By", b.toString()); + String b = createPoweredByHeader(); + theHttpResponse.addHeader("X-Powered-By", b); } private void addLocationHeader(RequestDetails theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation, String resourceName) { @@ -198,6 +209,33 @@ public class RestfulServer extends HttpServlet implements IRestfulServer createPoweredByAttributes() { + return Lists.newArrayList("FHIR Server", "FHIR " + myFhirContext.getVersion().getVersion().getFhirVersionString() + "/" + myFhirContext.getVersion().getVersion().name()); + } + + protected String createPoweredByHeader() { + StringBuilder b = new StringBuilder(); + b.append(createPoweredByHeaderProductName()); + b.append(" "); + b.append(VersionUtil.getVersion()); + b.append(" REST Server ("); + + List poweredByAttributes = createPoweredByAttributes(); + for (ListIterator iter = poweredByAttributes.listIterator(); iter.hasNext(); ) { + if (iter.nextIndex() > 0) { + b.append("; "); + } + b.append(iter.next()); + } + + b.append(")"); + return b.toString(); + } + + protected String createPoweredByHeaderProductName() { + return "HAPI FHIR"; + } + @Override public void destroy() { if (getResourceProviders() != null) { @@ -253,6 +291,31 @@ public class RestfulServer extends HttpServlet implements IRestfulServernull. Default is * {@link #DEFAULT_ETAG_SUPPORT} * - * @param theETagSupport - * The ETag support mode + * @param theETagSupport The ETag support mode */ public void setETagSupport(ETagSupportEnum theETagSupport) { if (theETagSupport == null) { @@ -477,8 +537,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer theList) { - myInterceptors.clear(); - if (theList != null) { - myInterceptors.addAll(theList); - } - } - @Override public IPagingProvider getPagingProvider() { return myPagingProvider; @@ -533,25 +579,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer * Note that this method can only be called before the server is initialized. * - * @throws IllegalStateException - * Note that this method can only be called prior to {@link #init() initialization} and will throw an - * {@link IllegalStateException} if called after that. + * @throws IllegalStateException Note that this method can only be called prior to {@link #init() initialization} and will throw an + * {@link IllegalStateException} if called after that. */ public void setServerConformanceProvider(Object theServerConformanceProvider) { if (myStarted) { @@ -670,9 +694,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServernull), the tenant identification - * strategy provides a mechanism for a multitenant server to identify which tenant - * a given request corresponds to. - */ - public void setTenantIdentificationStrategy(ITenantIdentificationStrategy theTenantIdentificationStrategy) { - myTenantIdentificationStrategy = theTenantIdentificationStrategy; - } - /** * Gets the server's name, as exported in conformance profiles exported by the server. This is informational only, * but can be helpful to set with something appropriate. @@ -1029,10 +1044,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer clazz) { + for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) { + Initialize initialize = m.getAnnotation(Initialize.class); + if (initialize != null) { + invokeInitializeOrDestroyMethod(theProvider, m, "initialize"); + } + } + + Class supertype = clazz.getSuperclass(); + if (!Object.class.equals(supertype)) { + invokeInitialize(theProvider, supertype); + } + } + private void invokeInitializeOrDestroyMethod(Object theProvider, Method m, String theMethodDescription) { Class[] paramTypes = m.getParameterTypes(); @@ -1078,24 +1110,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer clazz) { - for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) { - Initialize initialize = m.getAnnotation(Initialize.class); - if (initialize != null) { - invokeInitializeOrDestroyMethod(theProvider, m, "initialize"); - } - } - - Class supertype = clazz.getSuperclass(); - if (!Object.class.equals(supertype)) { - invokeInitialize(theProvider, supertype); - } - } - /** * Should the server "pretty print" responses by default (requesting clients can always override this default by * supplying an Accept header in the request, or a _pretty @@ -1119,8 +1133,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServerfalse *

* - * @param theDefaultPrettyPrint - * The default pretty print setting + * @param theDefaultPrettyPrint The default pretty print setting */ public void setDefaultPrettyPrint(boolean theDefaultPrettyPrint) { myDefaultPrettyPrint = theDefaultPrettyPrint; @@ -1174,8 +1187,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer providers) { myProviderRegistrationMutex.lock(); @@ -1327,7 +1339,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer clazz = theProvider.getClass(); Class supertype = clazz.getSuperclass(); @@ -1447,50 +1459,46 @@ public class RestfulServer extends HttpServlet implements IRestfulServer theList) { + myInterceptors.clear(); + if (theList != null) { + myInterceptors.addAll(theList); + } } - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - handleRequest(RequestTypeEnum.GET, request, response); - } - - @Override - protected void doOptions(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException { - handleRequest(RequestTypeEnum.OPTIONS, theReq, theResp); - } - - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - handleRequest(RequestTypeEnum.POST, request, response); - } - - @Override - protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - handleRequest(RequestTypeEnum.PUT, request, response); + /** + * Sets the non-resource specific providers which implement method calls on this server. + * + * @see #setResourceProviders(Collection) + */ + public void setPlainProviders(Object... theProv) { + setPlainProviders(Arrays.asList(theProv)); } /** @@ -1505,6 +1513,25 @@ public class RestfulServer extends HttpServlet implements IRestfulServernull), the tenant identification + * strategy provides a mechanism for a multitenant server to identify which tenant + * a given request corresponds to. + */ + public void setTenantIdentificationStrategy(ITenantIdentificationStrategy theTenantIdentificationStrategy) { + myTenantIdentificationStrategy = theTenantIdentificationStrategy; + } + public void unregisterInterceptor(IServerInterceptor theInterceptor) { Validate.notNull(theInterceptor, "Interceptor can not be null"); myInterceptors.remove(theInterceptor); @@ -1567,6 +1594,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer 0 && (nextString.charAt(0) == '_' || nextString.charAt(0) == '$' || nextString.equals(Constants.URL_TOKEN_METADATA)); + } + // /** // * Returns the read method binding for the given resource type, or // * returns null if not