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}