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