001package ca.uhn.fhir.rest.method;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2017 University Health Network
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022import static org.apache.commons.lang3.StringUtils.isBlank;
023
024import java.io.IOException;
025import java.io.InputStream;
026import java.io.Reader;
027import java.lang.reflect.InvocationTargetException;
028import java.lang.reflect.Method;
029import java.util.ArrayList;
030import java.util.Collection;
031import java.util.HashSet;
032import java.util.List;
033import java.util.Set;
034import java.util.TreeSet;
035
036import org.apache.commons.io.IOUtils;
037import org.hl7.fhir.instance.model.api.IAnyResource;
038import org.hl7.fhir.instance.model.api.IBaseResource;
039
040import ca.uhn.fhir.context.ConfigurationException;
041import ca.uhn.fhir.context.FhirContext;
042import ca.uhn.fhir.context.FhirVersionEnum;
043import ca.uhn.fhir.model.api.Bundle;
044import ca.uhn.fhir.model.api.IResource;
045import ca.uhn.fhir.model.api.Include;
046import ca.uhn.fhir.model.api.TagList;
047import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
048import ca.uhn.fhir.parser.IParser;
049import ca.uhn.fhir.rest.annotation.*;
050import ca.uhn.fhir.rest.api.MethodOutcome;
051import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
052import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
053import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
054import ca.uhn.fhir.rest.server.BundleProviders;
055import ca.uhn.fhir.rest.server.Constants;
056import ca.uhn.fhir.rest.server.EncodingEnum;
057import ca.uhn.fhir.rest.server.IBundleProvider;
058import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
059import ca.uhn.fhir.rest.server.IResourceProvider;
060import ca.uhn.fhir.rest.server.IRestfulServer;
061import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
062import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
063import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
064import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
065import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
066import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
067import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
068import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
069import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
070import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
071import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
072import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
073import ca.uhn.fhir.util.ReflectionUtil;
074
075public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T> {
076
077        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
078        private FhirContext myContext;
079        private Method myMethod;
080        private List<IParameter> myParameters;
081        private Object myProvider;
082        private boolean mySupportsConditional;
083        private boolean mySupportsConditionalMultiple;
084
085        public BaseMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
086                assert theMethod != null;
087                assert theContext != null;
088
089                myMethod = theMethod;
090                myContext = theContext;
091                myProvider = theProvider;
092                myParameters = MethodUtil.getResourceParameters(theContext, theMethod, theProvider, getRestOperationType());
093
094                for (IParameter next : myParameters) {
095                        if (next instanceof ConditionalParamBinder) {
096                                mySupportsConditional = true;
097                                if (((ConditionalParamBinder) next).isSupportsMultiple()) {
098                                        mySupportsConditionalMultiple = true;
099                                }
100                                break;
101                        }
102                }
103
104        }
105
106        protected IParser createAppropriateParserForParsingResponse(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, List<Class<? extends IBaseResource>> thePreferTypes) {
107                EncodingEnum encoding = EncodingEnum.forContentType(theResponseMimeType);
108                if (encoding == null) {
109                        NonFhirResponseException ex = NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
110                        populateException(ex, theResponseReader);
111                        throw ex;
112                }
113
114                IParser parser = encoding.newParser(getContext());
115                
116                parser.setPreferTypes(thePreferTypes);
117                
118                return parser;
119        }
120
121        protected IParser createAppropriateParserForParsingServerRequest(RequestDetails theRequest) {
122                String contentTypeHeader = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE);
123                EncodingEnum encoding;
124                if (isBlank(contentTypeHeader)) {
125                        encoding = EncodingEnum.XML;
126                } else {
127                        int semicolon = contentTypeHeader.indexOf(';');
128                        if (semicolon != -1) {
129                                contentTypeHeader = contentTypeHeader.substring(0, semicolon);
130                        }
131                        encoding = EncodingEnum.forContentType(contentTypeHeader);
132                }
133
134                if (encoding == null) {
135                        throw new InvalidRequestException("Request contins non-FHIR conent-type header value: " + contentTypeHeader);
136                }
137
138                IParser parser = encoding.newParser(getContext());
139                return parser;
140        }
141
142        protected Object[] createParametersForServerRequest(RequestDetails theRequest) {
143                Object[] params = new Object[getParameters().size()];
144                for (int i = 0; i < getParameters().size(); i++) {
145                        IParameter param = getParameters().get(i);
146                        if (param == null) {
147                                continue;
148                        }
149                        params[i] = param.translateQueryParametersIntoServerArgument(theRequest, this);
150                }
151                return params;
152        }
153
154        public List<Class<?>> getAllowableParamAnnotations() {
155                return null;
156        }
157
158        public FhirContext getContext() {
159                return myContext;
160        }
161
162        public Set<String> getIncludes() {
163                Set<String> retVal = new TreeSet<String>();
164                for (IParameter next : myParameters) {
165                        if (next instanceof IncludeParameter) {
166                                retVal.addAll(((IncludeParameter) next).getAllow());
167                        }
168                }
169                return retVal;
170        }
171
172        public Method getMethod() {
173                return myMethod;
174        }
175
176        public List<IParameter> getParameters() {
177                return myParameters;
178        }
179
180        public Object getProvider() {
181                return myProvider;
182        }
183
184        @SuppressWarnings({ "unchecked", "rawtypes" })
185        public Set<Include> getRequestIncludesFromParams(Object[] params) {
186                if (params == null || params.length == 0) {
187                        return null;
188                }
189                int index = 0;
190                boolean match = false;
191                for (IParameter parameter : myParameters) {
192                        if (parameter instanceof IncludeParameter) {
193                                match = true;
194                                break;
195                        }
196                        index++;
197                }
198                if (!match) {
199                        return null;
200                }
201                if (index >= params.length) {
202                        ourLog.warn("index out of parameter range (should never happen");
203                        return null;
204                }
205                if (params[index] instanceof Set) {
206                        return (Set<Include>) params[index];
207                }
208                if (params[index] instanceof Iterable) {
209                        Set includes = new HashSet<Include>();
210                        for (Object o : (Iterable) params[index]) {
211                                if (o instanceof Include) {
212                                        includes.add(o);
213                                }
214                        }
215                        return includes;
216                }
217                ourLog.warn("include params wasn't Set or Iterable, it was {}", params[index].getClass());
218                return null;
219        }
220
221        /**
222         * Returns the name of the resource this method handles, or <code>null</code> if this method is not resource specific
223         */
224        public abstract String getResourceName();
225
226        public abstract RestOperationTypeEnum getRestOperationType();
227
228        /**
229         * Determine which operation is being fired for a specific request
230         * 
231         * @param theRequestDetails
232         *           The request
233         */
234        public RestOperationTypeEnum getRestOperationType(RequestDetails theRequestDetails) {
235                return getRestOperationType();
236        }
237
238        public abstract boolean incomingServerRequestMatchesMethod(RequestDetails theRequest);
239
240        public abstract BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException;
241
242        public abstract Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException;
243
244        protected final Object invokeServerMethod(IRestfulServer<?> theServer, RequestDetails theRequest, Object[] theMethodParams) {
245                // Handle server action interceptors
246                RestOperationTypeEnum operationType = getRestOperationType(theRequest);
247                if (operationType != null) {
248                        for (IServerInterceptor next : theServer.getInterceptors()) {
249                                ActionRequestDetails details = new ActionRequestDetails(theRequest);
250                                populateActionRequestDetailsForInterceptor(theRequest, details, theMethodParams);
251                                next.incomingRequestPreHandled(operationType, details);
252                        }
253                }
254
255                // Actually invoke the method
256                try {
257                        Method method = getMethod();
258                        return method.invoke(getProvider(), theMethodParams);
259                } catch (InvocationTargetException e) {
260                        if (e.getCause() instanceof BaseServerResponseException) {
261                                throw (BaseServerResponseException) e.getCause();
262                        }
263                        throw new InternalErrorException("Failed to call access method", e);
264                } catch (Exception e) {
265                        throw new InternalErrorException("Failed to call access method", e);
266                }
267        }
268
269        /**
270         * Does this method have a parameter annotated with {@link ConditionalParamBinder}. Note that many operations don't actually support this paramter, so this will only return true occasionally.
271         */
272        public boolean isSupportsConditional() {
273                return mySupportsConditional;
274        }
275
276        /**
277         * Does this method support conditional operations over multiple objects (basically for conditional delete)
278         */
279        public boolean isSupportsConditionalMultiple() {
280                return mySupportsConditionalMultiple;
281        }
282
283        /**
284         * Subclasses may override this method (but should also call super.{@link #populateActionRequestDetailsForInterceptor(RequestDetails, ActionRequestDetails, Object[])} to provide method specifics to the
285         * interceptors.
286         * 
287         * @param theRequestDetails
288         *           The server request details
289         * @param theDetails
290         *           The details object to populate
291         * @param theMethodParams
292         *           The method params as generated by the specific method binding
293         */
294        protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails theDetails, Object[] theMethodParams) {
295                // nothing by default
296        }
297
298        protected BaseServerResponseException processNon2xxResponseAndReturnExceptionToThrow(int theStatusCode, String theResponseMimeType, Reader theResponseReader) {
299                BaseServerResponseException ex;
300                switch (theStatusCode) {
301                case Constants.STATUS_HTTP_400_BAD_REQUEST:
302                        ex = new InvalidRequestException("Server responded with HTTP 400");
303                        break;
304                case Constants.STATUS_HTTP_404_NOT_FOUND:
305                        ex = new ResourceNotFoundException("Server responded with HTTP 404");
306                        break;
307                case Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED:
308                        ex = new MethodNotAllowedException("Server responded with HTTP 405");
309                        break;
310                case Constants.STATUS_HTTP_409_CONFLICT:
311                        ex = new ResourceVersionConflictException("Server responded with HTTP 409");
312                        break;
313                case Constants.STATUS_HTTP_412_PRECONDITION_FAILED:
314                        ex = new PreconditionFailedException("Server responded with HTTP 412");
315                        break;
316                case Constants.STATUS_HTTP_422_UNPROCESSABLE_ENTITY:
317                        IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theStatusCode, null);
318                        // TODO: handle if something other than OO comes back
319                        BaseOperationOutcome operationOutcome = (BaseOperationOutcome) parser.parseResource(theResponseReader);
320                        ex = new UnprocessableEntityException(myContext, operationOutcome);
321                        break;
322                default:
323                        ex = new UnclassifiedServerFailureException(theStatusCode, "Server responded with HTTP " + theStatusCode);
324                        break;
325                }
326
327                populateException(ex, theResponseReader);
328                return ex;
329        }
330
331        /** For unit tests only */
332        public void setParameters(List<IParameter> theParameters) {
333                myParameters = theParameters;
334        }
335
336        protected IBundleProvider toResourceList(Object response) throws InternalErrorException {
337                if (response == null) {
338                        return BundleProviders.newEmptyList();
339                } else if (response instanceof IBundleProvider) {
340                        return (IBundleProvider) response;
341                } else if (response instanceof IBaseResource) {
342                        return BundleProviders.newList((IBaseResource) response);
343                } else if (response instanceof Collection) {
344                        List<IBaseResource> retVal = new ArrayList<IBaseResource>();
345                        for (Object next : ((Collection<?>) response)) {
346                                retVal.add((IBaseResource) next);
347                        }
348                        return BundleProviders.newList(retVal);
349                } else if (response instanceof MethodOutcome) {
350                        IBaseResource retVal = ((MethodOutcome) response).getOperationOutcome();
351                        if (retVal == null) {
352                                retVal = getContext().getResourceDefinition("OperationOutcome").newInstance();
353                        }
354                        return BundleProviders.newList(retVal);
355                } else {
356                        throw new InternalErrorException("Unexpected return type: " + response.getClass().getCanonicalName());
357                }
358        }
359
360        @SuppressWarnings("unchecked")
361        public static BaseMethodBinding<?> bindMethod(Method theMethod, FhirContext theContext, Object theProvider) {
362                Read read = theMethod.getAnnotation(Read.class);
363                Search search = theMethod.getAnnotation(Search.class);
364                Metadata conformance = theMethod.getAnnotation(Metadata.class);
365                Create create = theMethod.getAnnotation(Create.class);
366                Update update = theMethod.getAnnotation(Update.class);
367                Delete delete = theMethod.getAnnotation(Delete.class);
368                History history = theMethod.getAnnotation(History.class);
369                Validate validate = theMethod.getAnnotation(Validate.class);
370                GetTags getTags = theMethod.getAnnotation(GetTags.class);
371                AddTags addTags = theMethod.getAnnotation(AddTags.class);
372                DeleteTags deleteTags = theMethod.getAnnotation(DeleteTags.class);
373                Transaction transaction = theMethod.getAnnotation(Transaction.class);
374                Operation operation = theMethod.getAnnotation(Operation.class);
375                GetPage getPage = theMethod.getAnnotation(GetPage.class);
376                Patch patch = theMethod.getAnnotation(Patch.class);
377
378                // ** if you add another annotation above, also add it to the next line:
379                if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags, transaction, operation, getPage, patch)) {
380                        return null;
381                }
382
383                if (getPage != null) {
384                        return new PageMethodBinding(theContext, theMethod);
385                }
386                
387                Class<? extends IBaseResource> returnType;
388
389                Class<? extends IBaseResource> returnTypeFromRp = null;
390                if (theProvider instanceof IResourceProvider) {
391                        returnTypeFromRp = ((IResourceProvider) theProvider).getResourceType();
392                        if (!verifyIsValidResourceReturnType(returnTypeFromRp)) {
393                                throw new ConfigurationException("getResourceType() from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returned "
394                                                + toLogString(returnTypeFromRp) + " - Must return a resource type");
395                        }
396                }
397
398                Class<?> returnTypeFromMethod = theMethod.getReturnType();
399                if (getTags != null) {
400                        if (!TagList.class.equals(returnTypeFromMethod)) {
401                                throw new ConfigurationException("Method '" + theMethod.getName() + "' from type " + theMethod.getDeclaringClass().getCanonicalName() + " is annotated with @"
402                                                + GetTags.class.getSimpleName() + " but does not return type " + TagList.class.getName());
403                        }
404                } else if (MethodOutcome.class.isAssignableFrom(returnTypeFromMethod)) {
405                        // returns a method outcome
406                } else if (IBundleProvider.class.equals(returnTypeFromMethod)) {
407                        // returns a bundle provider
408                } else if (Bundle.class.equals(returnTypeFromMethod)) {
409                        // returns a bundle
410                } else if (void.class.equals(returnTypeFromMethod)) {
411                        // returns a bundle
412                } else if (Collection.class.isAssignableFrom(returnTypeFromMethod)) {
413                        returnTypeFromMethod = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(theMethod);
414                        if (returnTypeFromMethod == null) {
415                                ourLog.trace("Method {} returns a non-typed list, can't verify return type", theMethod);
416                        } else if (!verifyIsValidResourceReturnType(returnTypeFromMethod) && !isResourceInterface(returnTypeFromMethod)) {
417                                throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
418                                                + " returns a collection with generic type " + toLogString(returnTypeFromMethod)
419                                                + " - Must return a resource type or a collection (List, Set) with a resource type parameter (e.g. List<Patient> or List<IBaseResource> )");
420                        }
421                } else {
422                        if (!isResourceInterface(returnTypeFromMethod) && !verifyIsValidResourceReturnType(returnTypeFromMethod)) {
423                                throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
424                                                + " returns " + toLogString(returnTypeFromMethod) + " - Must return a resource type (eg Patient, " + Bundle.class.getSimpleName() + ", " + IBundleProvider.class.getSimpleName()
425                                                + ", etc., see the documentation for more details)");
426                        }
427                }
428
429                Class<? extends IBaseResource> returnTypeFromAnnotation = IBaseResource.class;
430                if (read != null) {
431                        returnTypeFromAnnotation = read.type();
432                } else if (search != null) {
433                        returnTypeFromAnnotation = search.type();
434                } else if (history != null) {
435                        returnTypeFromAnnotation = history.type();
436                } else if (delete != null) {
437                        returnTypeFromAnnotation = delete.type();
438                } else if (patch != null) {
439                        returnTypeFromAnnotation = patch.type();
440                } else if (create != null) {
441                        returnTypeFromAnnotation = create.type();
442                } else if (update != null) {
443                        returnTypeFromAnnotation = update.type();
444                } else if (validate != null) {
445                        returnTypeFromAnnotation = validate.type();
446                } else if (getTags != null) {
447                        returnTypeFromAnnotation = getTags.type();
448                } else if (addTags != null) {
449                        returnTypeFromAnnotation = addTags.type();
450                } else if (deleteTags != null) {
451                        returnTypeFromAnnotation = deleteTags.type();
452                }
453
454                if (returnTypeFromRp != null) {
455                        if (returnTypeFromAnnotation != null && !isResourceInterface(returnTypeFromAnnotation)) {
456                                if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
457                                        //FIXME potential null access on retunrTypeFromMethod
458                                        throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type "
459                                                        + returnTypeFromMethod.getCanonicalName() + " - Must return " + returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
460                                }
461                                if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
462                                        throw new ConfigurationException(
463                                                        "Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " claims to return type " + returnTypeFromAnnotation.getCanonicalName()
464                                                                        + " per method annotation - Must return " + returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
465                                }
466                                returnType = returnTypeFromAnnotation;
467                        } else {
468                                returnType = returnTypeFromRp;
469                        }
470                } else {
471                        if (!isResourceInterface(returnTypeFromAnnotation)) {
472                                if (!verifyIsValidResourceReturnType(returnTypeFromAnnotation)) {
473                                        throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
474                                                        + " returns " + toLogString(returnTypeFromAnnotation) + " according to annotation - Must return a resource type");
475                                }
476                                returnType = returnTypeFromAnnotation;
477                        } else {
478                                // if (IRestfulClient.class.isAssignableFrom(theMethod.getDeclaringClass())) {
479                                // Clients don't define their methods in resource specific types, so they can
480                                // infer their resource type from the method return type.
481                                returnType = (Class<? extends IBaseResource>) returnTypeFromMethod;
482                                // } else {
483                                // This is a plain provider method returning a resource, so it should be
484                                // an operation or global search presumably
485                                // returnType = null;
486                        }
487                }
488
489                if (read != null) {
490                        return new ReadMethodBinding(returnType, theMethod, theContext, theProvider);
491                } else if (search != null) {
492                        if (search.dynamic()) {
493                                IDynamicSearchResourceProvider provider = (IDynamicSearchResourceProvider) theProvider;
494                                return new DynamicSearchMethodBinding(returnType, theMethod, theContext, provider);
495                        }
496                        return new SearchMethodBinding(returnType, theMethod, theContext, theProvider);
497                } else if (conformance != null) {
498                        return new ConformanceMethodBinding(theMethod, theContext, theProvider);
499                } else if (create != null) {
500                        return new CreateMethodBinding(theMethod, theContext, theProvider);
501                } else if (update != null) {
502                        return new UpdateMethodBinding(theMethod, theContext, theProvider);
503                } else if (delete != null) {
504                        return new DeleteMethodBinding(theMethod, theContext, theProvider);
505                } else if (patch != null) {
506                        return new PatchMethodBinding(theMethod, theContext, theProvider);
507                } else if (history != null) {
508                        return new HistoryMethodBinding(theMethod, theContext, theProvider);
509                } else if (validate != null) {
510                        if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
511                                return new ValidateMethodBindingDstu1(theMethod, theContext, theProvider);
512                        }
513                        return new ValidateMethodBindingDstu2Plus(returnType, returnTypeFromRp, theMethod, theContext, theProvider, validate);
514                } else if (getTags != null) {
515                        return new GetTagsMethodBinding(theMethod, theContext, theProvider, getTags);
516                } else if (addTags != null) {
517                        return new AddTagsMethodBinding(theMethod, theContext, theProvider, addTags);
518                } else if (deleteTags != null) {
519                        return new DeleteTagsMethodBinding(theMethod, theContext, theProvider, deleteTags);
520                } else if (transaction != null) {
521                        return new TransactionMethodBinding(theMethod, theContext, theProvider);
522                } else if (operation != null) {
523                        return new OperationMethodBinding(returnType, returnTypeFromRp, theMethod, theContext, theProvider, operation);
524                } else {
525                        throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
526                }
527
528                // // each operation name must have a request type annotation and be
529                // unique
530                // if (null != read) {
531                // return rm;
532                // }
533                //
534                // SearchMethodBinding sm = new SearchMethodBinding();
535                // if (null != search) {
536                // sm.setRequestType(SearchMethodBinding.RequestType.GET);
537                // } else if (null != theMethod.getAnnotation(PUT.class)) {
538                // sm.setRequestType(SearchMethodBinding.RequestType.PUT);
539                // } else if (null != theMethod.getAnnotation(POST.class)) {
540                // sm.setRequestType(SearchMethodBinding.RequestType.POST);
541                // } else if (null != theMethod.getAnnotation(DELETE.class)) {
542                // sm.setRequestType(SearchMethodBinding.RequestType.DELETE);
543                // } else {
544                // return null;
545                // }
546                //
547                // return sm;
548        }
549
550        private static boolean isResourceInterface(Class<?> theReturnTypeFromMethod) {
551                return theReturnTypeFromMethod.equals(IBaseResource.class) || theReturnTypeFromMethod.equals(IResource.class) || theReturnTypeFromMethod.equals(IAnyResource.class);
552        }
553
554        private static void populateException(BaseServerResponseException theEx, Reader theResponseReader) {
555                try {
556                        String responseText = IOUtils.toString(theResponseReader);
557                        theEx.setResponseBody(responseText);
558                } catch (IOException e) {
559                        ourLog.debug("Failed to read response", e);
560                }
561        }
562
563        private static String toLogString(Class<?> theType) {
564                if (theType == null) {
565                        return null;
566                }
567                return theType.getCanonicalName();
568        }
569
570        private static boolean verifyIsValidResourceReturnType(Class<?> theReturnType) {
571                if (theReturnType == null) {
572                        return false;
573                }
574                if (!IBaseResource.class.isAssignableFrom(theReturnType)) {
575                        return false;
576                }
577                return true;
578                // boolean retVal = Modifier.isAbstract(theReturnType.getModifiers()) == false;
579                // return retVal;
580        }
581
582        public static boolean verifyMethodHasZeroOrOneOperationAnnotation(Method theNextMethod, Object... theAnnotations) {
583                Object obj1 = null;
584                for (Object object : theAnnotations) {
585                        if (object != null) {
586                                if (obj1 == null) {
587                                        obj1 = object;
588                                } else {
589                                        throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @"
590                                                        + obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName() + ". Can not have both.");
591                                }
592
593                        }
594                }
595                if (obj1 == null) {
596                        return false;
597                        // throw new ConfigurationException("Method '" +
598                        // theNextMethod.getName() + "' on type '" +
599                        // theNextMethod.getDeclaringClass().getSimpleName() +
600                        // " has no FHIR method annotations.");
601                }
602                return true;
603        }
604
605        /**
606         * @see ServletRequestDetails#getByteStreamRequestContents()
607         */
608        public static class ActiveRequestReader implements IRequestReader {
609                @Override
610                public InputStream getInputStream(RequestDetails theRequestDetails) throws IOException {
611                        return theRequestDetails.getInputStream();
612                }
613        }
614
615        /**
616         * @see ServletRequestDetails#getByteStreamRequestContents()
617         */
618        public static class InactiveRequestReader implements IRequestReader {
619                @Override
620                public InputStream getInputStream(RequestDetails theRequestDetails) {
621                        throw new IllegalStateException("The servlet-api JAR is not found on the classpath. Please check that this library is available.");
622                }
623        }
624
625        /**
626         * @see ServletRequestDetails#getByteStreamRequestContents()
627         */
628        public static interface IRequestReader {
629                InputStream getInputStream(RequestDetails theRequestDetails) throws IOException;
630        }
631
632}