001package org.hl7.fhir.dstu3.hapi.rest.server; 002 003import ca.uhn.fhir.context.FhirVersionEnum; 004import ca.uhn.fhir.context.RuntimeResourceDefinition; 005import ca.uhn.fhir.context.RuntimeSearchParam; 006import ca.uhn.fhir.parser.DataFormatException; 007import ca.uhn.fhir.rest.annotation.IdParam; 008import ca.uhn.fhir.rest.annotation.Metadata; 009import ca.uhn.fhir.rest.annotation.Read; 010import ca.uhn.fhir.rest.api.Constants; 011import ca.uhn.fhir.rest.api.server.RequestDetails; 012import ca.uhn.fhir.rest.server.*; 013import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; 014import ca.uhn.fhir.rest.server.method.*; 015import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType; 016import ca.uhn.fhir.rest.server.method.SearchParameter; 017import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider; 018import org.apache.commons.lang3.StringUtils; 019import org.hl7.fhir.dstu3.model.*; 020import org.hl7.fhir.dstu3.model.CapabilityStatement.*; 021import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; 022import org.hl7.fhir.dstu3.model.OperationDefinition.OperationDefinitionParameterComponent; 023import org.hl7.fhir.dstu3.model.OperationDefinition.OperationKind; 024import org.hl7.fhir.dstu3.model.OperationDefinition.OperationParameterUse; 025import org.hl7.fhir.exceptions.FHIRException; 026import org.hl7.fhir.instance.model.api.IBaseResource; 027import org.hl7.fhir.instance.model.api.IPrimitiveType; 028 029import javax.servlet.ServletContext; 030import javax.servlet.http.HttpServletRequest; 031import java.util.*; 032import java.util.Map.Entry; 033 034import static org.apache.commons.lang3.StringUtils.isBlank; 035import static org.apache.commons.lang3.StringUtils.isNotBlank; 036 037import ca.uhn.fhir.context.FhirContext; 038 039/* 040 * #%L 041 * HAPI FHIR Structures - DSTU2 (FHIR v1.0.0) 042 * %% 043 * Copyright (C) 2014 - 2015 University Health Network 044 * %% 045 * Licensed under the Apache License, Version 2.0 (the "License"); 046 * you may not use this file except in compliance with the License. 047 * You may obtain a copy of the License at 048 * 049 * http://www.apache.org/licenses/LICENSE-2.0 050 * 051 * Unless required by applicable law or agreed to in writing, software 052 * distributed under the License is distributed on an "AS IS" BASIS, 053 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 054 * See the License for the specific language governing permissions and 055 * limitations under the License. 056 * #L% 057 */ 058 059/** 060 * Server FHIR Provider which serves the conformance statement for a RESTful server implementation 061 * 062 * <p> 063 * Note: This class is safe to extend, but it is important to note that the same instance of {@link CapabilityStatement} is always returned unless {@link #setCache(boolean)} is called with a value of 064 * <code>false</code>. This means that if you are adding anything to the returned conformance instance on each call you should call <code>setCache(false)</code> in your provider constructor. 065 * </p> 066 */ 067public class ServerCapabilityStatementProvider extends BaseServerCapabilityStatementProvider implements IServerConformanceProvider<CapabilityStatement> { 068 069 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProvider.class); 070 private String myPublisher = "Not provided"; 071 072 /** 073 * No-arg constructor and setter so that the ServerConformanceProvider can be Spring-wired with the RestfulService avoiding the potential reference cycle that would happen. 074 */ 075 public ServerCapabilityStatementProvider() { 076 super(); 077 } 078 079 /** 080 * Constructor 081 * 082 * @deprecated Use no-args constructor instead. Deprecated in 4.0.0 083 */ 084 @Deprecated 085 public ServerCapabilityStatementProvider(RestfulServer theRestfulServer) { 086 this(); 087 } 088 089 /** 090 * Constructor - This is intended only for JAX-RS server 091 */ 092 public ServerCapabilityStatementProvider(RestfulServerConfiguration theServerConfiguration) { 093 super(theServerConfiguration); 094 } 095 096 private void checkBindingForSystemOps(CapabilityStatementRestComponent rest, Set<SystemRestfulInteraction> systemOps, BaseMethodBinding<?> nextMethodBinding) { 097 if (nextMethodBinding.getRestOperationType() != null) { 098 String sysOpCode = nextMethodBinding.getRestOperationType().getCode(); 099 if (sysOpCode != null) { 100 SystemRestfulInteraction sysOp; 101 try { 102 sysOp = SystemRestfulInteraction.fromCode(sysOpCode); 103 } catch (FHIRException e) { 104 return; 105 } 106 if (sysOp == null) { 107 return; 108 } 109 if (systemOps.contains(sysOp) == false) { 110 systemOps.add(sysOp); 111 rest.addInteraction().setCode(sysOp); 112 } 113 } 114 } 115 } 116 117 private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings(RequestDetails theRequestDetails) { 118 Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<>(); 119 for (ResourceBinding next : getServerConfiguration(theRequestDetails).getResourceBindings()) { 120 String resourceName = next.getResourceName(); 121 for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) { 122 if (resourceToMethods.containsKey(resourceName) == false) { 123 resourceToMethods.put(resourceName, new ArrayList<>()); 124 } 125 resourceToMethods.get(resourceName).add(nextMethodBinding); 126 } 127 } 128 for (BaseMethodBinding<?> nextMethodBinding : getServerConfiguration(theRequestDetails).getServerBindings()) { 129 String resourceName = ""; 130 if (resourceToMethods.containsKey(resourceName) == false) { 131 resourceToMethods.put(resourceName, new ArrayList<>()); 132 } 133 resourceToMethods.get(resourceName).add(nextMethodBinding); 134 } 135 return resourceToMethods; 136 } 137 138 private DateTimeType conformanceDate(RequestDetails theRequestDetails) { 139 IPrimitiveType<Date> buildDate = getServerConfiguration(theRequestDetails).getConformanceDate(); 140 if (buildDate != null && buildDate.getValue() != null) { 141 try { 142 return new DateTimeType(buildDate.getValueAsString()); 143 } catch (DataFormatException e) { 144 // fall through 145 } 146 } 147 return DateTimeType.now(); 148 } 149 150 private String createNamedQueryName(SearchMethodBinding searchMethodBinding) { 151 StringBuilder retVal = new StringBuilder(); 152 if (searchMethodBinding.getResourceName() != null) { 153 retVal.append(searchMethodBinding.getResourceName()); 154 } 155 retVal.append("-query-"); 156 retVal.append(searchMethodBinding.getQueryName()); 157 158 return retVal.toString(); 159 } 160 161 private String createOperationName(OperationMethodBinding theMethodBinding) { 162 StringBuilder retVal = new StringBuilder(); 163 if (theMethodBinding.getResourceName() != null) { 164 retVal.append(theMethodBinding.getResourceName()); 165 } 166 167 retVal.append('-'); 168 if (theMethodBinding.isCanOperateAtInstanceLevel()) { 169 retVal.append('i'); 170 } 171 if (theMethodBinding.isCanOperateAtServerLevel()) { 172 retVal.append('s'); 173 } 174 retVal.append('-'); 175 176 // Exclude the leading $ 177 retVal.append(theMethodBinding.getName(), 1, theMethodBinding.getName().length()); 178 179 return retVal.toString(); 180 } 181 182 /** 183 * Gets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The 184 * value defaults to "Not provided" but may be set to null, which will cause this element to be omitted. 185 */ 186 public String getPublisher() { 187 return myPublisher; 188 } 189 190 /** 191 * Sets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The 192 * value defaults to "Not provided" but may be set to null, which will cause this element to be omitted. 193 */ 194 public void setPublisher(String thePublisher) { 195 myPublisher = thePublisher; 196 } 197 198 199 @SuppressWarnings("EnumSwitchStatementWhichMissesCases") 200 @Override 201 @Metadata 202 public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) { 203 RestfulServerConfiguration serverConfiguration = getServerConfiguration(theRequestDetails); 204 Bindings bindings = serverConfiguration.provideBindings(); 205 206 CapabilityStatement retVal = new CapabilityStatement(); 207 208 retVal.setPublisher(myPublisher); 209 retVal.setDateElement(conformanceDate(theRequestDetails)); 210 retVal.setFhirVersion(FhirVersionEnum.DSTU3.getFhirVersionString()); 211 retVal.setAcceptUnknown(UnknownContentCode.EXTENSIONS); // TODO: make this configurable - this is a fairly big 212 // effort since the parser 213 // needs to be modified to actually allow it 214 215 ServletContext servletContext = (ServletContext) (theRequest == null ? null : theRequest.getAttribute(RestfulServer.SERVLET_CONTEXT_ATTRIBUTE)); 216 String serverBase = serverConfiguration.getServerAddressStrategy().determineServerBase(servletContext, theRequest); 217 retVal 218 .getImplementation() 219 .setUrl(serverBase) 220 .setDescription(serverConfiguration.getImplementationDescription()); 221 222 retVal.setKind(CapabilityStatementKind.INSTANCE); 223 retVal.getSoftware().setName(serverConfiguration.getServerName()); 224 retVal.getSoftware().setVersion(serverConfiguration.getServerVersion()); 225 retVal.addFormat(Constants.CT_FHIR_XML_NEW); 226 retVal.addFormat(Constants.CT_FHIR_JSON_NEW); 227 retVal.setStatus(PublicationStatus.ACTIVE); 228 229 CapabilityStatementRestComponent rest = retVal.addRest(); 230 rest.setMode(RestfulCapabilityMode.SERVER); 231 232 Set<SystemRestfulInteraction> systemOps = new HashSet<>(); 233 Set<String> operationNames = new HashSet<>(); 234 235 Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings(theRequestDetails); 236 Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = serverConfiguration.getNameToSharedSupertype(); 237 for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) { 238 239 if (nextEntry.getKey().isEmpty() == false) { 240 Set<TypeRestfulInteraction> resourceOps = new HashSet<>(); 241 CapabilityStatementRestResourceComponent resource = rest.addResource(); 242 String resourceName = nextEntry.getKey(); 243 244 RuntimeResourceDefinition def; 245 FhirContext context = serverConfiguration.getFhirContext(); 246 if (resourceNameToSharedSupertype.containsKey(resourceName)) { 247 def = context.getResourceDefinition(resourceNameToSharedSupertype.get(resourceName)); 248 } else { 249 def = context.getResourceDefinition(resourceName); 250 } 251 resource.getTypeElement().setValue(def.getName()); 252 resource.getProfile().setReference((def.getResourceProfile(serverBase))); 253 254 TreeSet<String> includes = new TreeSet<>(); 255 256 // Map<String, CapabilityStatement.RestResourceSearchParam> nameToSearchParam = new HashMap<String, 257 // CapabilityStatement.RestResourceSearchParam>(); 258 for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { 259 if (nextMethodBinding.getRestOperationType() != null) { 260 String resOpCode = nextMethodBinding.getRestOperationType().getCode(); 261 if (resOpCode != null) { 262 TypeRestfulInteraction resOp; 263 try { 264 resOp = TypeRestfulInteraction.fromCode(resOpCode); 265 } catch (Exception e) { 266 resOp = null; 267 } 268 if (resOp != null) { 269 if (resourceOps.contains(resOp) == false) { 270 resourceOps.add(resOp); 271 resource.addInteraction().setCode(resOp); 272 } 273 if ("vread".equals(resOpCode)) { 274 // vread implies read 275 resOp = TypeRestfulInteraction.READ; 276 if (resourceOps.contains(resOp) == false) { 277 resourceOps.add(resOp); 278 resource.addInteraction().setCode(resOp); 279 } 280 } 281 282 if (nextMethodBinding.isSupportsConditional()) { 283 switch (resOp) { 284 case CREATE: 285 resource.setConditionalCreate(true); 286 break; 287 case DELETE: 288 if (nextMethodBinding.isSupportsConditionalMultiple()) { 289 resource.setConditionalDelete(ConditionalDeleteStatus.MULTIPLE); 290 } else { 291 resource.setConditionalDelete(ConditionalDeleteStatus.SINGLE); 292 } 293 break; 294 case UPDATE: 295 resource.setConditionalUpdate(true); 296 break; 297 default: 298 break; 299 } 300 } 301 } 302 } 303 } 304 305 checkBindingForSystemOps(rest, systemOps, nextMethodBinding); 306 307 if (nextMethodBinding instanceof SearchMethodBinding) { 308 SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding; 309 if (methodBinding.getQueryName() != null) { 310 String queryName = bindings.getNamedSearchMethodBindingToName().get(methodBinding); 311 if (operationNames.add(queryName)) { 312 rest.addOperation().setName(methodBinding.getQueryName()).setDefinition(new Reference("OperationDefinition/" + queryName)); 313 } 314 } else { 315 handleNamelessSearchMethodBinding(rest, resource, resourceName, def, includes, (SearchMethodBinding) nextMethodBinding, theRequestDetails); 316 } 317 } else if (nextMethodBinding instanceof OperationMethodBinding) { 318 OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; 319 String opName = bindings.getOperationBindingToName().get(methodBinding); 320 if (operationNames.add(opName)) { 321 // Only add each operation (by name) once 322 rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition(new Reference("OperationDefinition/" + opName)); 323 } 324 } 325 326 resource.getInteraction().sort(new Comparator<ResourceInteractionComponent>() { 327 @Override 328 public int compare(ResourceInteractionComponent theO1, ResourceInteractionComponent theO2) { 329 TypeRestfulInteraction o1 = theO1.getCode(); 330 TypeRestfulInteraction o2 = theO2.getCode(); 331 if (o1 == null && o2 == null) { 332 return 0; 333 } 334 if (o1 == null) { 335 return 1; 336 } 337 if (o2 == null) { 338 return -1; 339 } 340 return o1.ordinal() - o2.ordinal(); 341 } 342 }); 343 344 } 345 346 for (String nextInclude : includes) { 347 resource.addSearchInclude(nextInclude); 348 } 349 } else { 350 for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { 351 checkBindingForSystemOps(rest, systemOps, nextMethodBinding); 352 if (nextMethodBinding instanceof OperationMethodBinding) { 353 OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; 354 String opName = bindings.getOperationBindingToName().get(methodBinding); 355 if (operationNames.add(opName)) { 356 ourLog.debug("Found bound operation: {}", opName); 357 rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition(new Reference("OperationDefinition/" + opName)); 358 } 359 } 360 } 361 } 362 } 363 364 return retVal; 365 } 366 367 368 369 private void handleNamelessSearchMethodBinding(CapabilityStatementRestComponent rest, CapabilityStatementRestResourceComponent resource, String resourceName, RuntimeResourceDefinition def, TreeSet<String> includes, 370 SearchMethodBinding searchMethodBinding, RequestDetails theRequestDetails) { 371 includes.addAll(searchMethodBinding.getIncludes()); 372 373 List<IParameter> params = searchMethodBinding.getParameters(); 374 List<SearchParameter> searchParameters = new ArrayList<>(); 375 for (IParameter nextParameter : params) { 376 if ((nextParameter instanceof SearchParameter)) { 377 searchParameters.add((SearchParameter) nextParameter); 378 } 379 } 380 sortSearchParameters(searchParameters); 381 if (!searchParameters.isEmpty()) { 382 // boolean allOptional = searchParameters.get(0).isRequired() == false; 383 // 384 // OperationDefinition query = null; 385 // if (!allOptional) { 386 // RestOperation operation = rest.addOperation(); 387 // query = new OperationDefinition(); 388 // operation.setDefinition(new ResourceReferenceDt(query)); 389 // query.getDescriptionElement().setValue(searchMethodBinding.getDescription()); 390 // query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName)); 391 // for (String nextInclude : searchMethodBinding.getIncludes()) { 392 // query.addUndeclaredExtension(false, ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude)); 393 // } 394 // } 395 396 for (SearchParameter nextParameter : searchParameters) { 397 398 String nextParamName = nextParameter.getName(); 399 400 String chain = null; 401 String nextParamUnchainedName = nextParamName; 402 if (nextParamName.contains(".")) { 403 chain = nextParamName.substring(nextParamName.indexOf('.') + 1); 404 nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.')); 405 } 406 407 String nextParamDescription = nextParameter.getDescription(); 408 409 /* 410 * If the parameter has no description, default to the one from the resource 411 */ 412 if (StringUtils.isBlank(nextParamDescription)) { 413 RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName); 414 if (paramDef != null) { 415 nextParamDescription = paramDef.getDescription(); 416 } 417 } 418 419 CapabilityStatementRestResourceSearchParamComponent param = resource.addSearchParam(); 420 param.setName(nextParamUnchainedName); 421 422// if (StringUtils.isNotBlank(chain)) { 423// param.addChain(chain); 424// } 425// 426// if (nextParameter.getParamType() == RestSearchParameterTypeEnum.REFERENCE) { 427// for (String nextWhitelist : new TreeSet<String>(nextParameter.getQualifierWhitelist())) { 428// if (nextWhitelist.startsWith(".")) { 429// param.addChain(nextWhitelist.substring(1)); 430// } 431// } 432// } 433 434 param.setDocumentation(nextParamDescription); 435 if (nextParameter.getParamType() != null) { 436 param.getTypeElement().setValueAsString(nextParameter.getParamType().getCode()); 437 } 438 for (Class<? extends IBaseResource> nextTarget : nextParameter.getDeclaredTypes()) { 439 RuntimeResourceDefinition targetDef = getServerConfiguration(theRequestDetails).getFhirContext().getResourceDefinition(nextTarget); 440 if (targetDef != null) { 441 ResourceType code; 442 try { 443 code = ResourceType.fromCode(targetDef.getName()); 444 } catch (FHIRException e) { 445 code = null; 446 } 447// if (code != null) { 448// param.addTarget(targetDef.getName()); 449// } 450 } 451 } 452 } 453 } 454 } 455 456 457 458 @Read(type = OperationDefinition.class) 459 public OperationDefinition readOperationDefinition(@IdParam IdType theId, RequestDetails theRequestDetails) { 460 if (theId == null || theId.hasIdPart() == false) { 461 throw new ResourceNotFoundException(theId); 462 } 463 464 RestfulServerConfiguration serverConfiguration = getServerConfiguration(theRequestDetails); 465 Bindings bindings = serverConfiguration.provideBindings(); 466 467 List<OperationMethodBinding> operationBindings = bindings.getOperationNameToBindings().get(theId.getIdPart()); 468 if (operationBindings != null && !operationBindings.isEmpty()) { 469 return readOperationDefinitionForOperation(operationBindings); 470 } 471 List<SearchMethodBinding> searchBindings = bindings.getSearchNameToBindings().get(theId.getIdPart()); 472 if (searchBindings != null && !searchBindings.isEmpty()) { 473 return readOperationDefinitionForNamedSearch(searchBindings); 474 } 475 throw new ResourceNotFoundException(theId); 476 } 477 478 private OperationDefinition readOperationDefinitionForNamedSearch(List<SearchMethodBinding> bindings) { 479 OperationDefinition op = new OperationDefinition(); 480 op.setStatus(PublicationStatus.ACTIVE); 481 op.setKind(OperationKind.QUERY); 482 op.setIdempotent(true); 483 484 op.setSystem(false); 485 op.setType(false); 486 op.setInstance(false); 487 488 Set<String> inParams = new HashSet<>(); 489 490 for (SearchMethodBinding binding : bindings) { 491 if (isNotBlank(binding.getDescription())) { 492 op.setDescription(binding.getDescription()); 493 } 494 if (isBlank(binding.getResourceProviderResourceName())) { 495 op.setSystem(true); 496 } else { 497 op.setType(true); 498 op.addResourceElement().setValue(binding.getResourceProviderResourceName()); 499 } 500 op.setCode(binding.getQueryName()); 501 for (IParameter nextParamUntyped : binding.getParameters()) { 502 if (nextParamUntyped instanceof SearchParameter) { 503 SearchParameter nextParam = (SearchParameter) nextParamUntyped; 504 if (!inParams.add(nextParam.getName())) { 505 continue; 506 } 507 OperationDefinitionParameterComponent param = op.addParameter(); 508 param.setUse(OperationParameterUse.IN); 509 param.setType("string"); 510 param.getSearchTypeElement().setValueAsString(nextParam.getParamType().getCode()); 511 param.setMin(nextParam.isRequired() ? 1 : 0); 512 param.setMax("1"); 513 param.setName(nextParam.getName()); 514 } 515 } 516 517 if (isBlank(op.getName())) { 518 if (isNotBlank(op.getDescription())) { 519 op.setName(op.getDescription()); 520 } else { 521 op.setName(op.getCode()); 522 } 523 } 524 } 525 526 return op; 527 } 528 529 private OperationDefinition readOperationDefinitionForOperation(List<OperationMethodBinding> bindings) { 530 OperationDefinition op = new OperationDefinition(); 531 op.setStatus(PublicationStatus.ACTIVE); 532 op.setKind(OperationKind.OPERATION); 533 op.setIdempotent(true); 534 535 // We reset these to true below if we find a binding that can handle the level 536 op.setSystem(false); 537 op.setType(false); 538 op.setInstance(false); 539 540 Set<String> inParams = new HashSet<>(); 541 Set<String> outParams = new HashSet<>(); 542 543 for (OperationMethodBinding sharedDescription : bindings) { 544 if (isNotBlank(sharedDescription.getDescription())) { 545 op.setDescription(sharedDescription.getDescription()); 546 } 547 if (sharedDescription.isCanOperateAtInstanceLevel()) { 548 op.setInstance(true); 549 } 550 if (sharedDescription.isCanOperateAtServerLevel()) { 551 op.setSystem(true); 552 } 553 if (sharedDescription.isCanOperateAtTypeLevel()) { 554 op.setType(true); 555 } 556 if (!sharedDescription.isIdempotent()) { 557 op.setIdempotent(sharedDescription.isIdempotent()); 558 } 559 op.setCode(sharedDescription.getName().substring(1)); 560 if (sharedDescription.isCanOperateAtInstanceLevel()) { 561 op.setInstance(sharedDescription.isCanOperateAtInstanceLevel()); 562 } 563 if (sharedDescription.isCanOperateAtServerLevel()) { 564 op.setSystem(sharedDescription.isCanOperateAtServerLevel()); 565 } 566 if (isNotBlank(sharedDescription.getResourceName())) { 567 op.addResourceElement().setValue(sharedDescription.getResourceName()); 568 } 569 570 for (IParameter nextParamUntyped : sharedDescription.getParameters()) { 571 if (nextParamUntyped instanceof OperationParameter) { 572 OperationParameter nextParam = (OperationParameter) nextParamUntyped; 573 OperationDefinitionParameterComponent param = op.addParameter(); 574 if (!inParams.add(nextParam.getName())) { 575 continue; 576 } 577 param.setUse(OperationParameterUse.IN); 578 if (nextParam.getParamType() != null) { 579 param.setType(nextParam.getParamType()); 580 } 581 if (nextParam.getSearchParamType() != null) { 582 param.getSearchTypeElement().setValueAsString(nextParam.getSearchParamType()); 583 } 584 param.setMin(nextParam.getMin()); 585 param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax())); 586 param.setName(nextParam.getName()); 587 } 588 } 589 590 for (ReturnType nextParam : sharedDescription.getReturnParams()) { 591 if (!outParams.add(nextParam.getName())) { 592 continue; 593 } 594 OperationDefinitionParameterComponent param = op.addParameter(); 595 param.setUse(OperationParameterUse.OUT); 596 if (nextParam.getType() != null) { 597 param.setType(nextParam.getType()); 598 } 599 param.setMin(nextParam.getMin()); 600 param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax())); 601 param.setName(nextParam.getName()); 602 } 603 } 604 605 if (isBlank(op.getName())) { 606 if (isNotBlank(op.getDescription())) { 607 op.setName(op.getDescription()); 608 } else { 609 op.setName(op.getCode()); 610 } 611 } 612 613 if (op.hasSystem() == false) { 614 op.setSystem(false); 615 } 616 if (op.hasInstance() == false) { 617 op.setInstance(false); 618 } 619 620 return op; 621 } 622 623 /** 624 * Sets the cache property (default is true). If set to true, the same response will be returned for each invocation. 625 * <p> 626 * See the class documentation for an important note if you are extending this class 627 * </p> 628 * 629 * @deprecated Since 4.0.0 this doesn't do anything 630 */ 631 public ServerCapabilityStatementProvider setCache(boolean theCache) { 632 return this; 633 } 634 635 @Override 636 public void setRestfulServer(RestfulServer theRestfulServer) { 637 // ignore 638 } 639 640 private void sortSearchParameters(List<SearchParameter> searchParameters) { 641 Collections.sort(searchParameters, new Comparator<SearchParameter>() { 642 @Override 643 public int compare(SearchParameter theO1, SearchParameter theO2) { 644 if (theO1.isRequired() == theO2.isRequired()) { 645 return theO1.getName().compareTo(theO2.getName()); 646 } 647 if (theO1.isRequired()) { 648 return -1; 649 } 650 return 1; 651 } 652 }); 653 } 654}