Refactor information message

This commit is contained in:
James Agnew 2018-05-16 17:35:40 -04:00
parent e187d72a3b
commit 833cfd6988
1 changed files with 208 additions and 177 deletions

View File

@ -19,42 +19,62 @@ package ca.uhn.fhir.rest.server;
* limitations under the License. * limitations under the License.
* #L% * #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.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.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.context.*; import javax.servlet.ServletException;
import ca.uhn.fhir.context.api.AddProfileTagEnum; import javax.servlet.UnavailableException;
import ca.uhn.fhir.context.api.BundleInclusionRule; import javax.servlet.http.HttpServlet;
import ca.uhn.fhir.parser.IParser; import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.rest.annotation.*; import javax.servlet.http.HttpServletResponse;
import ca.uhn.fhir.rest.api.*; import java.io.Closeable;
import ca.uhn.fhir.rest.api.server.*; import java.io.IOException;
import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding; import java.io.InputStream;
import ca.uhn.fhir.rest.server.exceptions.*; import java.io.Writer;
import ca.uhn.fhir.rest.server.interceptor.*; import java.lang.annotation.Annotation;
import ca.uhn.fhir.rest.server.method.*; import java.lang.reflect.Method;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import java.lang.reflect.Modifier;
import ca.uhn.fhir.util.*; 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") @SuppressWarnings("WeakerAccess")
public class RestfulServer extends HttpServlet implements IRestfulServer<ServletRequestDetails> { public class RestfulServer extends HttpServlet implements IRestfulServer<ServletRequestDetails> {
@ -96,7 +116,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
private BaseMethodBinding<?> myServerConformanceMethod; private BaseMethodBinding<?> myServerConformanceMethod;
private Object myServerConformanceProvider; private Object myServerConformanceProvider;
private String myServerName = "HAPI FHIR Server"; 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 String myServerVersion = VersionUtil.getVersion();
private boolean myStarted; private boolean myStarted;
private Map<String, IResourceProvider> myTypeToProvider = new HashMap<>(); private Map<String, IResourceProvider> myTypeToProvider = new HashMap<>();
@ -121,10 +143,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
myFhirContext = theCtx; myFhirContext = theCtx;
} }
private static boolean partIsOperation(String nextString) {
return nextString.length() > 0 && (nextString.charAt(0) == '_' || nextString.charAt(0) == '$' || nextString.equals(Constants.URL_TOKEN_METADATA));
}
private void addContentLocationHeaders(RequestDetails theRequest, HttpServletResponse servletResponse, MethodOutcome response, String resourceName) { private void addContentLocationHeaders(RequestDetails theRequest, HttpServletResponse servletResponse, MethodOutcome response, String resourceName) {
if (response != null && response.getId() != null) { if (response != null && response.getId() != null) {
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_LOCATION, resourceName); addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_LOCATION, resourceName);
@ -140,15 +158,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* </p> * </p>
*/ */
public void addHeadersToResponse(HttpServletResponse theHttpResponse) { public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
StringBuilder b = new StringBuilder(); String b = createPoweredByHeader();
b.append("HAPI FHIR "); theHttpResponse.addHeader("X-Powered-By", b);
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());
} }
private void addLocationHeader(RequestDetails theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation, String resourceName) { private void addLocationHeader(RequestDetails theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation, String resourceName) {
@ -198,6 +209,33 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return result; return result;
} }
protected List<String> 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<String> poweredByAttributes = createPoweredByAttributes();
for (ListIterator<String> 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 @Override
public void destroy() { public void destroy() {
if (getResourceProviders() != null) { if (getResourceProviders() != null) {
@ -253,6 +291,31 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return resourceMethod; return resourceMethod;
} }
@Override
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
handleRequest(RequestTypeEnum.DELETE, request, response);
}
@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);
}
/** /**
* Count length of URL string, but treating unescaped sequences (e.g. ' ') as their unescaped equivalent (%20) * Count length of URL string, but treating unescaped sequences (e.g. ' ') as their unescaped equivalent (%20)
*/ */
@ -369,8 +432,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* (which is the default), the server will automatically add a profile tag based on * (which is the default), the server will automatically add a profile tag based on
* the class of the resource(s) being returned. * the class of the resource(s) being returned.
* *
* @param theAddProfileTag * @param theAddProfileTag The behaviour enum (must not be null)
* The behaviour enum (must not be null)
* @deprecated As of HAPI FHIR 1.5, this property has been moved to * @deprecated As of HAPI FHIR 1.5, this property has been moved to
* {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)} * {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
*/ */
@ -389,8 +451,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/** /**
* Set how bundle factory should decide whether referenced resources should be included in bundles * Set how bundle factory should decide whether referenced resources should be included in bundles
* *
* @param theBundleInclusionRule * @param theBundleInclusionRule - inclusion rule (@see BundleInclusionRule for behaviors)
* - inclusion rule (@see BundleInclusionRule for behaviors)
*/ */
public void setBundleInclusionRule(BundleInclusionRule theBundleInclusionRule) { public void setBundleInclusionRule(BundleInclusionRule theBundleInclusionRule) {
myBundleInclusionRule = theBundleInclusionRule; myBundleInclusionRule = theBundleInclusionRule;
@ -429,8 +490,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* Sets (enables/disables) the server support for ETags. Must not be <code>null</code>. Default is * Sets (enables/disables) the server support for ETags. Must not be <code>null</code>. Default is
* {@link #DEFAULT_ETAG_SUPPORT} * {@link #DEFAULT_ETAG_SUPPORT}
* *
* @param theETagSupport * @param theETagSupport The ETag support mode
* The ETag support mode
*/ */
public void setETagSupport(ETagSupportEnum theETagSupport) { public void setETagSupport(ETagSupportEnum theETagSupport) {
if (theETagSupport == null) { if (theETagSupport == null) {
@ -477,8 +537,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/** /**
* Sets (or clears) the list of interceptors * Sets (or clears) the list of interceptors
* *
* @param theList * @param theList The list of interceptors (may be null)
* The list of interceptors (may be null)
*/ */
public void setInterceptors(IServerInterceptor... theList) { public void setInterceptors(IServerInterceptor... theList) {
myInterceptors.clear(); myInterceptors.clear();
@ -487,19 +546,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
} }
} }
/**
* Sets (or clears) the list of interceptors
*
* @param theList
* The list of interceptors (may be null)
*/
public void setInterceptors(List<IServerInterceptor> theList) {
myInterceptors.clear();
if (theList != null) {
myInterceptors.addAll(theList);
}
}
@Override @Override
public IPagingProvider getPagingProvider() { public IPagingProvider getPagingProvider() {
return myPagingProvider; return myPagingProvider;
@ -533,25 +579,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
} }
} }
/**
* 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));
}
/** /**
* Allows users of RestfulServer to override the getRequestPath method to let them build their custom request path * Allows users of RestfulServer to override the getRequestPath method to let them build their custom request path
* implementation * implementation
* *
* @param requestFullPath * @param requestFullPath the full request path
* the full request path * @param servletContextPath the servelet context path
* @param servletContextPath * @param servletPath the servelet path
* the servelet context path
* @param servletPath
* the servelet path
* @return created resource path * @return created resource path
*/ */
protected String getRequestPath(String requestFullPath, String servletContextPath, String servletPath) { protected String getRequestPath(String requestFullPath, String servletContextPath, String servletPath) {
@ -579,16 +613,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
} }
} }
/**
* Sets the resource providers for this server
*/
public void setResourceProviders(IResourceProvider... theResourceProviders) {
myResourceProviders.clear();
if (theResourceProviders != null) {
myResourceProviders.addAll(Arrays.asList(theResourceProviders));
}
}
/** /**
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this * Get the server address strategy, which is used to determine what base URL to provide clients to refer to this
* server. Defaults to an instance of {@link IncomingRequestAddressStrategy} * server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
@ -608,6 +632,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/** /**
* Returns the server base URL (with no trailing '/') for a given request * Returns the server base URL (with no trailing '/') for a given request
*
* @param theRequest * @param theRequest
*/ */
public String getServerBaseForRequest(ServletRequestDetails theRequest) { public String getServerBaseForRequest(ServletRequestDetails theRequest) {
@ -656,8 +681,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* </p> * </p>
* Note that this method can only be called before the server is initialized. * Note that this method can only be called before the server is initialized.
* *
* @throws IllegalStateException * @throws IllegalStateException Note that this method can only be called prior to {@link #init() initialization} and will throw an
* Note that this method can only be called prior to {@link #init() initialization} and will throw an
* {@link IllegalStateException} if called after that. * {@link IllegalStateException} if called after that.
*/ */
public void setServerConformanceProvider(Object theServerConformanceProvider) { public void setServerConformanceProvider(Object theServerConformanceProvider) {
@ -680,15 +704,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
myServerConformanceProvider = theServerConformanceProvider; myServerConformanceProvider = theServerConformanceProvider;
} }
/**
* If provided (default is <code>null</code>), 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, * 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. * but can be helpful to set with something appropriate.
@ -1029,8 +1044,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the * This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the
* server being used. * server being used.
* *
* @throws ServletException * @throws ServletException If the initialization failed. Note that you should consider throwing {@link UnavailableException}
* If the initialization failed. Note that you should consider throwing {@link UnavailableException}
* (which extends {@link ServletException}), as this is a flag to the servlet container * (which extends {@link ServletException}), as this is a flag to the servlet container
* that the servlet is not usable. * that the servlet is not usable.
*/ */
@ -1056,6 +1070,24 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
} }
} }
private void invokeInitialize(Object theProvider) {
invokeInitialize(theProvider, theProvider.getClass());
}
private void invokeInitialize(Object theProvider, Class<?> 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) { private void invokeInitializeOrDestroyMethod(Object theProvider, Method m, String theMethodDescription) {
Class<?>[] paramTypes = m.getParameterTypes(); Class<?>[] paramTypes = m.getParameterTypes();
@ -1078,24 +1110,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
} }
} }
private void invokeInitialize(Object theProvider) {
invokeInitialize(theProvider, theProvider.getClass());
}
private void invokeInitialize(Object theProvider, Class<?> 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 * Should the server "pretty print" responses by default (requesting clients can always override this default by
* supplying an <code>Accept</code> header in the request, or a <code>_pretty</code> * supplying an <code>Accept</code> header in the request, or a <code>_pretty</code>
@ -1119,8 +1133,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* The default is <code>false</code> * The default is <code>false</code>
* </p> * </p>
* *
* @param theDefaultPrettyPrint * @param theDefaultPrettyPrint The default pretty print setting
* The default pretty print setting
*/ */
public void setDefaultPrettyPrint(boolean theDefaultPrettyPrint) { public void setDefaultPrettyPrint(boolean theDefaultPrettyPrint) {
myDefaultPrettyPrint = theDefaultPrettyPrint; myDefaultPrettyPrint = theDefaultPrettyPrint;
@ -1285,8 +1298,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/** /**
* Register a group of providers. These could be Resource Providers, "plain" providers or a mixture of the two. * Register a group of providers. These could be Resource Providers, "plain" providers or a mixture of the two.
* *
* @param providers * @param providers a {@code Collection} of providers. The parameter could be null or an empty {@code Collection}
* a {@code Collection} of providers. The parameter could be null or an empty {@code Collection}
*/ */
public void registerProviders(Collection<? extends Object> providers) { public void registerProviders(Collection<? extends Object> providers) {
myProviderRegistrationMutex.lock(); myProviderRegistrationMutex.lock();
@ -1376,7 +1388,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/* /*
* Remove registered RESTful methods for a Provider (and all superclasses) when it is being unregistered * Remove registered RESTful methods for a Provider (and all superclasses) when it is being unregistered
*/ */
private void removeResourceMethods(Object theProvider) throws Exception { private void removeResourceMethods(Object theProvider) {
ourLog.info("Removing RESTful methods for: {}", theProvider.getClass()); ourLog.info("Removing RESTful methods for: {}", theProvider.getClass());
Class<?> clazz = theProvider.getClass(); Class<?> clazz = theProvider.getClass();
Class<?> supertype = clazz.getSuperclass(); Class<?> supertype = clazz.getSuperclass();
@ -1468,29 +1480,25 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
} }
} }
@Override /**
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { * Sets (or clears) the list of interceptors
handleRequest(RequestTypeEnum.DELETE, request, response); *
* @param theList The list of interceptors (may be null)
*/
public void setInterceptors(List<IServerInterceptor> theList) {
myInterceptors.clear();
if (theList != null) {
myInterceptors.addAll(theList);
}
} }
@Override /**
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { * Sets the non-resource specific providers which implement method calls on this server.
handleRequest(RequestTypeEnum.GET, request, response); *
} * @see #setResourceProviders(Collection)
*/
@Override public void setPlainProviders(Object... theProv) {
protected void doOptions(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException { setPlainProviders(Arrays.asList(theProv));
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);
} }
/** /**
@ -1505,6 +1513,25 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
} }
} }
/**
* Sets the resource providers for this server
*/
public void setResourceProviders(IResourceProvider... theResourceProviders) {
myResourceProviders.clear();
if (theResourceProviders != null) {
myResourceProviders.addAll(Arrays.asList(theResourceProviders));
}
}
/**
* If provided (default is <code>null</code>), 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) { public void unregisterInterceptor(IServerInterceptor theInterceptor) {
Validate.notNull(theInterceptor, "Interceptor can not be null"); Validate.notNull(theInterceptor, "Interceptor can not be null");
myInterceptors.remove(theInterceptor); myInterceptors.remove(theInterceptor);
@ -1567,6 +1594,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
theResponse.getWriter().write(theException.getMessage()); theResponse.getWriter().write(theException.getMessage());
} }
private static boolean partIsOperation(String nextString) {
return nextString.length() > 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 the read method binding for the given resource type, or
// * returns <code>null</code> if not // * returns <code>null</code> if not