From 14befcd09144e77840ac97d18ab0947bff41e414 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Wed, 2 Jul 2014 08:57:07 -0400 Subject: [PATCH] Lots of tester enhancements --- hapi-fhir-base/.classpath | 11 +- hapi-fhir-base/.project | 13 + .../.settings/org.eclipse.jdt.core.prefs | 8 + .../org.eclipse.wst.common.component | 6 + ....eclipse.wst.common.project.facet.core.xml | 5 + hapi-fhir-base/pom.xml | 13 +- hapi-fhir-base/src/changes/changes.xml | 3 + .../java/ca/uhn/fhir/context/FhirContext.java | 25 +- .../ca/uhn/fhir/context/ModelScanner.java | 25 +- .../model/api/ResourceMetadataKeyEnum.java | 104 +- .../ca/uhn/fhir/rest/client/BaseClient.java | 1 + .../rest/client/RestfulClientFactory.java | 99 +- .../BaseOutcomeReturningMethodBinding.java | 13 +- .../BaseResourceReturningMethodBinding.java | 6 +- .../java/ca/uhn/fhir/rest/method/Request.java | 10 +- .../fhir/rest/method/UpdateMethodBinding.java | 28 +- .../ca/uhn/fhir/rest/server/Constants.java | 4 + .../uhn/fhir/rest/server/RestfulServer.java | 97 +- .../BaseServerResponseException.java | 1 + .../exceptions/ResourceGoneException.java | 54 + .../tester/RestfulServerTesterServlet.java | 508 --------- .../server/tester/RestfulTesterServlet.java | 899 --------------- .../fhir/rest/server/tester/PublicTester.css | 60 - .../fhir/rest/server/tester/PublicTester.html | 159 --- .../fhir/rest/server/tester/PublicTester.js | 360 ------ .../server/tester/PublicTesterResult.html | 120 -- .../rest/server/tester/RestfulTester.html | 1008 ----------------- .../server/tester/img/hapi_fhir_banner.png | Bin 15132 -> 0 bytes .../tester/img/hapi_fhir_banner_right.png | Bin 5130 -> 0 bytes .../rest/server/tester/js/jquery-2.1.0.min.js | 4 - .../ca/uhn/fhir/rest/server/tester/json2.js | 11 - .../fhir/rest/server/tester/minify.json.js | 61 - .../fhir/rest/server/tester/shBrushJScript.js | 52 - .../fhir/rest/server/tester/shBrushPlain.js | 33 - .../uhn/fhir/rest/server/tester/shBrushXml.js | 69 -- .../ca/uhn/fhir/rest/server/tester/shCore.css | 226 ---- .../ca/uhn/fhir/rest/server/tester/shCore.js | 17 - .../fhir/rest/server/tester/shCoreDefault.css | 328 ------ .../rest/server/tester/shThemeDefault.css | 117 -- .../ca/uhn/fhir/context/FhirContextTest.java | 21 + .../ca/uhn/fhir/parser/JsonParserTest.java | 5 +- .../fhir/rest/client/GenericClientTest.java | 6 +- .../uhn/fhir/rest/server/CompressionTest.java | 151 +++ .../ca/uhn/fhir/rest/server/PagingTest.java | 29 +- .../ca/uhn/fhir/rest/server/TesterTest.java | 263 ----- .../src/test/resources/conformance.xsd | 779 ------------- .../src/test/resources/xmlexample.xml | 1 - hapi-fhir-jpaserver-base/.classpath | 3 +- hapi-fhir-jpaserver-base/.project | 13 + .../.settings/org.eclipse.jdt.core.prefs | 5 + .../org.eclipse.wst.common.component | 6 + ....eclipse.wst.common.project.facet.core.xml | 5 + hapi-fhir-jpaserver-base/pom.xml | 26 +- .../java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java | 161 +-- .../ca/uhn/fhir/jpa/dao/FhirResourceDao.java | 31 +- .../ca/uhn/fhir/jpa/dao/FhirSystemDao.java | 2 +- .../ca/uhn/fhir/jpa/dao/IFhirResourceDao.java | 2 + .../uhn/fhir/jpa/entity/BaseHasResource.java | 26 +- .../fhir/jpa/entity/ResourceHistoryTag.java | 1 + .../ca/uhn/fhir/jpa/entity/ResourceTable.java | 5 +- .../jpa/provider/JpaResourceProvider.java | 46 +- .../src/test/java/META-INF/MANIFEST.MF | 3 + .../uhn/fhir/jpa/dao/FhirResourceDaoTest.java | 152 ++- hapi-fhir-jpaserver-test/pom.xml | 4 +- .../resources/fhir-spring-test-config.xml | 0 .../resources/fhir_jpatest_persistence.xml | 0 .../src/{test => main}/resources/logback.xml | 0 .../resources/resources/patient-example-a.xml | 0 .../resources/patient-example-animal.xml | 0 .../resources/resources/patient-example-b.xml | 0 .../resources/patient-example-dicom.xml | 0 .../resources/patient-example-f001-pieter.xml | 0 .../resources/patient-example-f201-roel.xml | 0 .../resources/patient-example-ihe-pcd.xml | 0 .../patient-example-us-extensions.xml | 0 .../resources/patient-example-xcda.xml | 0 .../resources/patient-example-xds.xml | 0 .../resources/resources/patient-example.xml | 0 .../questionnaire-example-bluebook.xml | 0 .../questionnaire-example-f201-lifelines.xml | 0 .../resources/questionnaire-example.xml | 0 .../resources/test-server-seed-bundle.json | 0 .../test/CompleteResourceProviderTest.java | 6 - .../java/ca/uhn/fhir/jpa/test/SystemTest.java | 7 - hapi-fhir-jpaserver-uhnfhirtest/.classpath | 4 +- hapi-fhir-jpaserver-uhnfhirtest/.gitignore | 1 + hapi-fhir-jpaserver-uhnfhirtest/.project | 19 + .../.settings/.jsdtscope | 13 + .../.settings/org.eclipse.jdt.core.prefs | 7 +- .../org.eclipse.wst.common.component | 24 + ....eclipse.wst.common.project.facet.core.xml | 7 + ...rg.eclipse.wst.jsdt.ui.superType.container | 1 + .../org.eclipse.wst.jsdt.ui.superType.name | 1 + .../org.eclipse.wst.validation.prefs | 2 + hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 120 +- .../ca/uhn/fhirtest/DerbyNetworkServer.java | 30 + .../java/ca/uhn/fhirtest/HsqldbServer.java | 73 ++ .../ca/uhn/fhirtest/TestRestfulServer.java | 45 +- .../java/ca/uhn/fhirtest/TesterServlet.java | 18 - .../src/main/resources/logback-test.xml | 16 + .../WEB-INF/hapi-fhir-server-config.xml} | 19 - .../hapi-fhir-server-database-config.xml | 52 + .../WEB-INF/hapi-fhir-tester-config.xml | 35 + .../webapp/WEB-INF/templates/tmpl-footer.html | 16 + .../src/main/webapp/WEB-INF/web.xml | 41 +- .../xsd/javaee_web_services_client_1_3.xsd | 737 ------------ .../java/ca/uhn/fhirtest/UhnFhirTestApp.java | 100 ++ hapi-fhir-testpage-overlay/.classpath | 36 + hapi-fhir-testpage-overlay/.gitignore | 1 + hapi-fhir-testpage-overlay/.project | 42 + .../.settings/.jsdtscope | 13 + .../org.eclipse.core.resources.prefs | 4 + .../.settings/org.eclipse.jdt.core.prefs | 8 + .../.settings/org.eclipse.m2e.core.prefs | 4 + .../org.eclipse.wst.common.component | 14 + ....eclipse.wst.common.project.facet.core.xml | 7 + ...rg.eclipse.wst.jsdt.ui.superType.container | 1 + .../org.eclipse.wst.jsdt.ui.superType.name | 1 + .../org.eclipse.wst.validation.prefs | 2 + hapi-fhir-testpage-overlay/pom.xml | 196 ++++ .../main/java/ca/uhn/fhir/to/Controller.java | 832 ++++++++++++++ .../java/ca/uhn/fhir/to/TesterConfig.java | 39 + .../ca/uhn/fhir/to/model/HomeRequest.java | 107 ++ .../ca/uhn/fhir/to/model/ResourceRequest.java | 29 + .../hapi-fhir-tester-application-context.xml | 34 + .../WEB-INF/hapi-fhir-tester-config.xml | 32 + .../src/main/webapp/WEB-INF/templates/.swp | Bin 0 -> 12288 bytes .../main/webapp/WEB-INF/templates/home.html | 192 ++++ .../webapp/WEB-INF/templates/resource.html | 482 ++++++++ .../main/webapp/WEB-INF/templates/result.html | 268 +++++ .../webapp/WEB-INF/templates/tmpl-banner.html | 20 + .../webapp/WEB-INF/templates/tmpl-footer.html | 5 + .../webapp/WEB-INF/templates/tmpl-head.html | 38 + .../WEB-INF/templates/tmpl-navbar-left.html | 85 ++ .../WEB-INF/templates/tmpl-navbar-top.html | 51 + .../src/main/webapp/WEB-INF/web.xml | 44 + .../css/bootstrap-datetimepicker.min.css | 0 .../src/main/webapp}/css/bootstrap.min.css | 0 .../src/main/webapp}/css/select2-spinner.gif | Bin .../src/main/webapp}/css/select2.css | 0 .../src/main/webapp}/css/select2.png | Bin .../src/main/webapp}/css/select2x2.png | Bin .../src/main/webapp}/css/tester.css | 0 .../src/main/webapp}/fa/css/font-awesome.css | 0 .../main/webapp}/fa/css/font-awesome.min.css | 0 .../src/main/webapp}/fa/fonts/FontAwesome.otf | Bin .../webapp}/fa/fonts/fontawesome-webfont.eot | Bin .../webapp}/fa/fonts/fontawesome-webfont.svg | 0 .../webapp}/fa/fonts/fontawesome-webfont.ttf | Bin .../webapp}/fa/fonts/fontawesome-webfont.woff | Bin .../main/webapp}/fa/less/bordered-pulled.less | 0 .../src/main/webapp}/fa/less/core.less | 0 .../src/main/webapp}/fa/less/fixed-width.less | 0 .../main/webapp}/fa/less/font-awesome.less | 0 .../src/main/webapp}/fa/less/icons.less | 0 .../src/main/webapp}/fa/less/larger.less | 0 .../src/main/webapp}/fa/less/list.less | 0 .../src/main/webapp}/fa/less/mixins.less | 0 .../src/main/webapp}/fa/less/path.less | 0 .../main/webapp}/fa/less/rotated-flipped.less | 0 .../src/main/webapp}/fa/less/spinning.less | 0 .../src/main/webapp}/fa/less/stacked.less | 0 .../src/main/webapp}/fa/less/variables.less | 0 .../webapp}/fa/scss/_bordered-pulled.scss | 0 .../src/main/webapp}/fa/scss/_core.scss | 0 .../main/webapp}/fa/scss/_fixed-width.scss | 0 .../src/main/webapp}/fa/scss/_icons.scss | 0 .../src/main/webapp}/fa/scss/_larger.scss | 0 .../src/main/webapp}/fa/scss/_list.scss | 0 .../src/main/webapp}/fa/scss/_mixins.scss | 0 .../src/main/webapp}/fa/scss/_path.scss | 0 .../webapp}/fa/scss/_rotated-flipped.scss | 0 .../src/main/webapp}/fa/scss/_spinning.scss | 0 .../src/main/webapp}/fa/scss/_stacked.scss | 0 .../src/main/webapp}/fa/scss/_variables.scss | 0 .../main/webapp}/fa/scss/font-awesome.scss | 0 .../fonts/glyphicons-halflings-regular.eot | Bin .../fonts/glyphicons-halflings-regular.svg | 0 .../fonts/glyphicons-halflings-regular.ttf | Bin .../fonts/glyphicons-halflings-regular.woff | Bin .../src/main/webapp/img}/hapi_fhir_banner.png | Bin .../webapp/img}/hapi_fhir_banner_right.png | Bin .../src/main/webapp}/js/RestfulTester.js | 117 +- .../js/bootstrap-datetimepicker.min.js | 0 .../src/main/webapp}/js/bootstrap.min.js | 0 .../src/main/webapp/js}/jquery-2.1.0.min.js | 0 .../src/main/webapp}/js/moment.min.js | 0 .../src/main/webapp}/js/select2.min.js | 0 .../ca/uhn/fhir/jpa/test/OverlayTestApp.java | 48 +- pom.xml | 24 +- restful-server-example/.classpath | 25 +- restful-server-example/.project | 52 +- restful-server-example/.settings/.jsdtscope | 13 + .../.settings/org.eclipse.jdt.core.prefs | 9 +- .../.settings/org.eclipse.m2e.core.prefs | 4 + .../org.eclipse.wst.common.component | 12 + ....eclipse.wst.common.project.facet.core.xml | 7 + ...rg.eclipse.wst.jsdt.ui.superType.container | 1 + .../org.eclipse.wst.jsdt.ui.superType.name | 1 + .../org.eclipse.wst.validation.prefs | 2 + .../src/main/webapp/WEB-INF/web-app_3_0.xsd | 272 ----- .../src/main/webapp/WEB-INF/web.xml | 3 +- 202 files changed, 4333 insertions(+), 6542 deletions(-) create mode 100644 hapi-fhir-base/.settings/org.eclipse.wst.common.component create mode 100644 hapi-fhir-base/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceGoneException.java delete mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/RestfulServerTesterServlet.java delete mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/RestfulTesterServlet.java delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.css delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.html delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.js delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTesterResult.html delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/RestfulTester.html delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/img/hapi_fhir_banner.png delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/img/hapi_fhir_banner_right.png delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/js/jquery-2.1.0.min.js delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/json2.js delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/minify.json.js delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/shBrushJScript.js delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/shBrushPlain.js delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/shBrushXml.js delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/shCore.css delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/shCore.js delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/shCoreDefault.css delete mode 100644 hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/shThemeDefault.css create mode 100644 hapi-fhir-base/src/test/java/ca/uhn/fhir/context/FhirContextTest.java create mode 100644 hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/CompressionTest.java delete mode 100644 hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TesterTest.java delete mode 100644 hapi-fhir-base/src/test/resources/conformance.xsd create mode 100644 hapi-fhir-jpaserver-base/.settings/org.eclipse.wst.common.component create mode 100644 hapi-fhir-jpaserver-base/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 hapi-fhir-jpaserver-base/src/test/java/META-INF/MANIFEST.MF rename hapi-fhir-jpaserver-test/src/{test => main}/resources/fhir-spring-test-config.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/fhir_jpatest_persistence.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/logback.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/patient-example-a.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/patient-example-animal.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/patient-example-b.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/patient-example-dicom.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/patient-example-f001-pieter.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/patient-example-f201-roel.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/patient-example-ihe-pcd.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/patient-example-us-extensions.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/patient-example-xcda.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/patient-example-xds.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/patient-example.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/questionnaire-example-bluebook.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/questionnaire-example-f201-lifelines.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/resources/questionnaire-example.xml (100%) rename hapi-fhir-jpaserver-test/src/{test => main}/resources/test-server-seed-bundle.json (100%) create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/.settings/.jsdtscope create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/.settings/org.eclipse.wst.common.component create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/.settings/org.eclipse.wst.jsdt.ui.superType.container create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/.settings/org.eclipse.wst.jsdt.ui.superType.name create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/.settings/org.eclipse.wst.validation.prefs create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/DerbyNetworkServer.java create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/HsqldbServer.java delete mode 100644 hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TesterServlet.java create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/src/main/resources/logback-test.xml rename hapi-fhir-jpaserver-uhnfhirtest/src/main/{resources/fhir-spring-uhnfhirtest-config.xml => webapp/WEB-INF/hapi-fhir-server-config.xml} (58%) create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/templates/tmpl-footer.html delete mode 100644 hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/xsd/javaee_web_services_client_1_3.xsd create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java create mode 100644 hapi-fhir-testpage-overlay/.classpath create mode 100644 hapi-fhir-testpage-overlay/.gitignore create mode 100644 hapi-fhir-testpage-overlay/.project create mode 100644 hapi-fhir-testpage-overlay/.settings/.jsdtscope create mode 100644 hapi-fhir-testpage-overlay/.settings/org.eclipse.core.resources.prefs create mode 100644 hapi-fhir-testpage-overlay/.settings/org.eclipse.jdt.core.prefs create mode 100644 hapi-fhir-testpage-overlay/.settings/org.eclipse.m2e.core.prefs create mode 100644 hapi-fhir-testpage-overlay/.settings/org.eclipse.wst.common.component create mode 100644 hapi-fhir-testpage-overlay/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 hapi-fhir-testpage-overlay/.settings/org.eclipse.wst.jsdt.ui.superType.container create mode 100644 hapi-fhir-testpage-overlay/.settings/org.eclipse.wst.jsdt.ui.superType.name create mode 100644 hapi-fhir-testpage-overlay/.settings/org.eclipse.wst.validation.prefs create mode 100644 hapi-fhir-testpage-overlay/pom.xml create mode 100644 hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java create mode 100644 hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/TesterConfig.java create mode 100644 hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/model/HomeRequest.java create mode 100644 hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/model/ResourceRequest.java create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/hapi-fhir-tester-application-context.xml create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/.swp create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/home.html create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/resource.html create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/result.html create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-banner.html create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-footer.html create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-head.html create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-navbar-left.html create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-navbar-top.html create mode 100644 hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/web.xml rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/css/bootstrap-datetimepicker.min.css (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/css/bootstrap.min.css (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/css/select2-spinner.gif (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/css/select2.css (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/css/select2.png (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/css/select2x2.png (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/css/tester.css (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/css/font-awesome.css (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/css/font-awesome.min.css (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/fonts/FontAwesome.otf (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/fonts/fontawesome-webfont.eot (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/fonts/fontawesome-webfont.svg (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/fonts/fontawesome-webfont.ttf (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/fonts/fontawesome-webfont.woff (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/bordered-pulled.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/core.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/fixed-width.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/font-awesome.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/icons.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/larger.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/list.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/mixins.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/path.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/rotated-flipped.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/spinning.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/stacked.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/less/variables.less (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_bordered-pulled.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_core.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_fixed-width.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_icons.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_larger.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_list.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_mixins.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_path.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_rotated-flipped.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_spinning.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_stacked.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/_variables.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fa/scss/font-awesome.scss (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fonts/glyphicons-halflings-regular.eot (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fonts/glyphicons-halflings-regular.svg (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fonts/glyphicons-halflings-regular.ttf (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/fonts/glyphicons-halflings-regular.woff (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp/img}/hapi_fhir_banner.png (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp/img}/hapi_fhir_banner_right.png (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/js/RestfulTester.js (69%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/js/bootstrap-datetimepicker.min.js (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/js/bootstrap.min.js (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp/js}/jquery-2.1.0.min.js (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/js/moment.min.js (100%) rename {hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester => hapi-fhir-testpage-overlay/src/main/webapp}/js/select2.min.js (100%) rename hapi-fhir-jpaserver-test/src/test/java/ca/uhn/fhir/jpa/test/JpaTestApp.java => hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/OverlayTestApp.java (87%) create mode 100644 restful-server-example/.settings/.jsdtscope create mode 100644 restful-server-example/.settings/org.eclipse.m2e.core.prefs create mode 100644 restful-server-example/.settings/org.eclipse.wst.common.component create mode 100644 restful-server-example/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 restful-server-example/.settings/org.eclipse.wst.jsdt.ui.superType.container create mode 100644 restful-server-example/.settings/org.eclipse.wst.jsdt.ui.superType.name create mode 100644 restful-server-example/.settings/org.eclipse.wst.validation.prefs delete mode 100644 restful-server-example/src/main/webapp/WEB-INF/web-app_3_0.xsd diff --git a/hapi-fhir-base/.classpath b/hapi-fhir-base/.classpath index 534b5e52fa5..f0a60b623ce 100644 --- a/hapi-fhir-base/.classpath +++ b/hapi-fhir-base/.classpath @@ -22,14 +22,15 @@ - - - - - + + + + + + diff --git a/hapi-fhir-base/.project b/hapi-fhir-base/.project index 6ce81acf746..a47a949c8a6 100644 --- a/hapi-fhir-base/.project +++ b/hapi-fhir-base/.project @@ -5,6 +5,11 @@ + + org.eclipse.wst.common.project.facet.core.builder + + + org.eclipse.jdt.core.javabuilder @@ -15,9 +20,17 @@ + + org.eclipse.wst.validation.validationbuilder + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature + org.eclipse.wst.common.project.facet.core.nature diff --git a/hapi-fhir-base/.settings/org.eclipse.jdt.core.prefs b/hapi-fhir-base/.settings/org.eclipse.jdt.core.prefs index 60105c1b951..5ce45188994 100644 --- a/hapi-fhir-base/.settings/org.eclipse.jdt.core.prefs +++ b/hapi-fhir-base/.settings/org.eclipse.jdt.core.prefs @@ -1,5 +1,13 @@ eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.6 diff --git a/hapi-fhir-base/.settings/org.eclipse.wst.common.component b/hapi-fhir-base/.settings/org.eclipse.wst.common.component new file mode 100644 index 00000000000..1a94c8cf11d --- /dev/null +++ b/hapi-fhir-base/.settings/org.eclipse.wst.common.component @@ -0,0 +1,6 @@ + + + + + + diff --git a/hapi-fhir-base/.settings/org.eclipse.wst.common.project.facet.core.xml b/hapi-fhir-base/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 00000000000..5c9bd7532ab --- /dev/null +++ b/hapi-fhir-base/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 714f0495039..4e03be0d3bc 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -55,7 +55,7 @@ org.thymeleaf thymeleaf - 2.1.3.RELEASE + ${thymeleaf-version} true @@ -101,6 +101,13 @@ 4.3.2 + + org.springframework + spring-beans + ${spring_version} + true + + javax.servlet @@ -381,7 +388,9 @@ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - ga('create', 'UA-1395874-5', 'sourceforge.net'); + ga('create', 'UA-1395874-5', 'auto'); + ga('require', 'displayfeatures'); + ga('require', 'linkid', 'linkid.js'); ga('send', 'pageview'); diff --git a/hapi-fhir-base/src/changes/changes.xml b/hapi-fhir-base/src/changes/changes.xml index 432008964f4..651c8735c9a 100644 --- a/hapi-fhir-base/src/changes/changes.xml +++ b/hapi-fhir-base/src/changes/changes.xml @@ -60,6 +60,9 @@ Don't fail on narrative blocks in JSON resources with only an XML declaration but no content (these are produced by the Health Intersections server) + + Server now automatically compresses responses if the client indicates support + diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index a4212a3110f..e55941992b1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -66,7 +66,7 @@ public class FhirContext { private volatile INarrativeGenerator myNarrativeGenerator; private volatile IRestfulClientFactory myRestfulClientFactory; private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition; - + /** * Default constructor. In most cases this is the right constructor to use. */ @@ -77,7 +77,7 @@ public class FhirContext { public FhirContext(Class theResourceType) { this(toCollection(theResourceType)); } - + public FhirContext(Class... theResourceTypes) { this(toCollection(theResourceTypes)); } @@ -92,10 +92,6 @@ public class FhirContext { public BaseRuntimeElementDefinition getElementDefinition(Class theElementType) { return myClassToElementDefinition.get(theElementType); } - - public FhirTerser newTerser() { - return new FhirTerser(this); - } public INarrativeGenerator getNarrativeGenerator() { return myNarrativeGenerator; @@ -111,7 +107,7 @@ public class FhirContext { } return retVal; } - + /** * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library. */ @@ -226,6 +222,10 @@ public class FhirContext { return getRestfulClientFactory().newGenericClient(theServerBase); } + public FhirTerser newTerser() { + return new FhirTerser(this); + } + /** * Create and return a new JSON parser. * @@ -241,6 +241,10 @@ public class FhirContext { myNarrativeGenerator = theNarrativeGenerator; } + public void setRestfulClientFactory(IRestfulClientFactory theRestfulClientFactory) { + myRestfulClientFactory = theRestfulClientFactory; + } + private RuntimeResourceDefinition scanResourceType(Class theResourceType) { ArrayList> resourceTypes = new ArrayList>(); resourceTypes.add(theResourceType); @@ -249,7 +253,7 @@ public class FhirContext { } private Map, BaseRuntimeElementDefinition> scanResourceTypes(Collection> theResourceTypes) { - ModelScanner scanner = new ModelScanner(theResourceTypes); + ModelScanner scanner = new ModelScanner(myClassToElementDefinition, theResourceTypes); if (myRuntimeChildUndeclaredExtensionDefinition == null) { myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition(); } @@ -273,6 +277,11 @@ public class FhirContext { return classToElementDefinition; } + /** For unit tests only */ + int getElementDefinitionCount() { + return myClassToElementDefinition.size(); + } + private static Collection> toCollection(Class theResourceType) { ArrayList> retVal = new ArrayList>(1); retVal.add(theResourceType); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java index 8d669ba3938..3f14b9b40cd 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java @@ -35,6 +35,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.TreeMap; @@ -90,11 +91,15 @@ class ModelScanner { ModelScanner(Class theResourceTypes) throws ConfigurationException { Set> singleton = new HashSet>(); singleton.add(theResourceTypes); - init(singleton); + init(null,singleton); } ModelScanner(Collection> theResourceTypes) throws ConfigurationException { - init(new HashSet>(theResourceTypes)); + init(null, new HashSet>(theResourceTypes)); + } + + ModelScanner(Map, BaseRuntimeElementDefinition> theExistingDefinitions, Collection> theResourceTypes) throws ConfigurationException { + init(theExistingDefinitions, new HashSet>(theResourceTypes)); } public Map, BaseRuntimeElementDefinition> getClassToElementDefinitions() { @@ -147,7 +152,12 @@ class ModelScanner { } } - private void init(Set> toScan) { + private void init(Map, BaseRuntimeElementDefinition> theExistingDefinitions, Set> toScan) { + if (theExistingDefinitions!=null) { + myClassToElementDefinitions.putAll(theExistingDefinitions); + } + + int startSize = myClassToElementDefinitions.size(); long start = System.currentTimeMillis(); InputStream str = ModelScanner.class.getResourceAsStream("/ca/uhn/fhir/model/dstu/model.properties"); @@ -199,7 +209,11 @@ class ModelScanner { myScanAlso.clear(); } while (!toScan.isEmpty()); - for (BaseRuntimeElementDefinition next : myClassToElementDefinitions.values()) { + for (Entry, BaseRuntimeElementDefinition> nextEntry : myClassToElementDefinitions.entrySet()) { + if (theExistingDefinitions!=null&&theExistingDefinitions.containsKey(nextEntry.getKey())) { + continue; + } + BaseRuntimeElementDefinition next = nextEntry.getValue(); next.sealAndInitialize(myClassToElementDefinitions); } @@ -207,7 +221,8 @@ class ModelScanner { myRuntimeChildUndeclaredExtensionDefinition.sealAndInitialize(myClassToElementDefinitions); long time = System.currentTimeMillis() - start; - ourLog.info("Done scanning FHIR library, found {} model entries in {}ms", myClassToElementDefinitions.size(), time); + int size = myClassToElementDefinitions.size()- startSize; + ourLog.info("Done scanning FHIR library, found {} model entries in {}ms", size, time); } private void scan(Class theClass) throws ConfigurationException { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java index 459b360ce6a..b445058651c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java @@ -30,7 +30,6 @@ import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; public abstract class ResourceMetadataKeyEnum { - /** * If present and populated with a date/time (as an instance of {@link InstantDt}), @@ -99,8 +98,7 @@ public abstract class ResourceMetadataKeyEnum { theResource.getResourceMetadata().put(PUBLISHED, theObject); } }; - - + /** * The value for this key is the list of tags associated with this resource *

@@ -129,7 +127,7 @@ public abstract class ResourceMetadataKeyEnum { public void put(IResource theResource, TagList theObject) { theResource.getResourceMetadata().put(TAG_LIST, theObject); } - }; + }; /** @@ -153,7 +151,8 @@ public abstract class ResourceMetadataKeyEnum { public void put(IResource theResource, InstantDt theObject) { theResource.getResourceMetadata().put(UPDATED, theObject); } - }; + }; + /** * The value for this key is the version ID of the resource object. @@ -175,22 +174,14 @@ public abstract class ResourceMetadataKeyEnum { theResource.getResourceMetadata().put(VERSION_ID, theObject); } }; - - private final String myValue; - + private final String myValue; public ResourceMetadataKeyEnum(String theValue) { myValue = theValue; } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((myValue == null) ? 0 : myValue.hashCode()); - return result; - } + @Override public boolean equals(Object obj) { @@ -209,50 +200,63 @@ public abstract class ResourceMetadataKeyEnum { return true; } - private static IdDt getIdFromMetadataOrNullIfNone(Map, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) { - Object retValObj = theResourceMetadata.get(theKey); - if (retValObj == null) { - return null; - } else if (retValObj instanceof String) { - if (isNotBlank((String) retValObj)) { - return new IdDt((String) retValObj); - } else { - return null; - } - } else if (retValObj instanceof IdDt) { - if (((IdDt) retValObj).isEmpty()) { - return null; - } else { - return (IdDt) retValObj; - } - } else if (retValObj instanceof Number) { - return new IdDt(((Number)retValObj).toString()); - } - throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName()); + public abstract T get(IResource theResource); + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((myValue == null) ? 0 : myValue.hashCode()); + return result; + } + + public abstract void put(IResource theResource, T theObject); + + @Override + public String toString() { + return myValue; } private String name() { return myValue; } - private static InstantDt getInstantFromMetadataOrNullIfNone(Map, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) { - Object retValObj = theResourceMetadata.get(theKey); - if (retValObj == null) { - return null; - } else if (retValObj instanceof Date) { - return new InstantDt((Date) retValObj); - } else if (retValObj instanceof InstantDt) { - if (((InstantDt) retValObj).isEmpty()) { + private static IdDt getIdFromMetadataOrNullIfNone(Map, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) { + Object retValObj = theResourceMetadata.get(theKey); + if (retValObj == null) { return null; - } else { - return (InstantDt) retValObj; + } else if (retValObj instanceof String) { + if (isNotBlank((String) retValObj)) { + return new IdDt((String) retValObj); + } else { + return null; + } + } else if (retValObj instanceof IdDt) { + if (((IdDt) retValObj).isEmpty()) { + return null; + } else { + return (IdDt) retValObj; + } + } else if (retValObj instanceof Number) { + return new IdDt(((Number)retValObj).toString()); } + throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName()); } - throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName()); - } - - public abstract T get(IResource theResource); - public abstract void put(IResource theResource, T theObject); + private static InstantDt getInstantFromMetadataOrNullIfNone(Map, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) { + Object retValObj = theResourceMetadata.get(theKey); + if (retValObj == null) { + return null; + } else if (retValObj instanceof Date) { + return new InstantDt((Date) retValObj); + } else if (retValObj instanceof InstantDt) { + if (((InstantDt) retValObj).isEmpty()) { + return null; + } else { + return (InstantDt) retValObj; + } + } + throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName()); + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java index d30eee7ca25..86da2c6a2ad 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java @@ -151,6 +151,7 @@ public abstract class BaseClient { list.add(next.getValue()); } } + if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) { String body=null; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java index a0ce83d7e63..4002d79d4ea 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java @@ -28,7 +28,8 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.http.client.HttpClient; -import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import ca.uhn.fhir.context.ConfigurationException; @@ -38,9 +39,12 @@ import ca.uhn.fhir.rest.method.BaseMethodBinding; public class RestfulClientFactory implements IRestfulClientFactory { + private int myConnectionRequestTimeout=10000; + private int myConnectTimeout=10000; private FhirContext myContext; private HttpClient myHttpClient; private Map, ClientInvocationHandler> myInvocationHandlers = new HashMap, ClientInvocationHandler>(); + private int mySocketTimeout = 10000; /** * Constructor @@ -48,14 +52,63 @@ public class RestfulClientFactory implements IRestfulClientFactory { * @param theContext * The context */ - public RestfulClientFactory(FhirContext theContext) { + public RestfulClientFactory(FhirContext theFhirContext) { + myContext=theFhirContext; + } + + /** + * Constructor + */ + public RestfulClientFactory() { + } + + /** + * Sets the context associated with this client factory. Must not be called + * more than once. + */ + public void setFhirContext(FhirContext theContext) { + if(myContext!=null&&myContext!=theContext) { + throw new IllegalStateException("RestfulClientFactory instance is already associated with one FhirContext. RestfulClientFactory instances can not be shared."); + } myContext = theContext; } - @SuppressWarnings("unchecked") - private T instantiateProxy(Class theClientType, InvocationHandler theInvocationHandler) { - T proxy = (T) Proxy.newProxyInstance(theClientType.getClassLoader(), new Class[] { theClientType }, theInvocationHandler); - return proxy; + public int getConnectionRequestTimeout() { + return myConnectionRequestTimeout; + } + + public int getConnectTimeout() { + return myConnectTimeout; + } + + @Override + public synchronized HttpClient getHttpClient() { + if (myHttpClient == null) { + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + + //@formatter:off + RequestConfig defaultRequestConfig = RequestConfig.custom() + .setSocketTimeout(mySocketTimeout) + .setConnectTimeout(myConnectTimeout) + .setConnectionRequestTimeout(myConnectionRequestTimeout) + .setStaleConnectionCheckEnabled(true) + .build(); + + myHttpClient = HttpClients.custom() + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(defaultRequestConfig) + .disableCookieManagement() + .build(); + //@formatter:on + + } + + return myHttpClient; + } + + public int getSocketTimeout() { + return mySocketTimeout; } /** @@ -86,7 +139,7 @@ public class RestfulClientFactory implements IRestfulClientFactory { if (invocationHandler == null) { invocationHandler = new ClientInvocationHandler(client, myContext, serverBase, theClientType); for (Method nextMethod : theClientType.getMethods()) { - BaseMethodBinding binding = BaseMethodBinding.bindMethod(nextMethod, myContext, null); + BaseMethodBinding binding = BaseMethodBinding.bindMethod(nextMethod, myContext, null); invocationHandler.addBinding(nextMethod, binding); } myInvocationHandlers.put(theClientType, invocationHandler); @@ -98,14 +151,18 @@ public class RestfulClientFactory implements IRestfulClientFactory { } @Override - public synchronized HttpClient getHttpClient() { - if (myHttpClient == null) { - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - myHttpClient = builder.build(); - } - return myHttpClient; + public IGenericClient newGenericClient(String theServerBase) { + return new GenericClient(myContext, getHttpClient(), theServerBase); + } + + public synchronized void setConnectionRequestTimeout(int theConnectionRequestTimeout) { + myConnectionRequestTimeout = theConnectionRequestTimeout; + myHttpClient=null; + } + + public synchronized void setConnectTimeout(int theConnectTimeout) { + myConnectTimeout = theConnectTimeout; + myHttpClient=null; } /** @@ -120,9 +177,15 @@ public class RestfulClientFactory implements IRestfulClientFactory { myHttpClient = theHttpClient; } - @Override - public IGenericClient newGenericClient(String theServerBase) { - return new GenericClient(myContext, getHttpClient(), theServerBase); + public synchronized void setSocketTimeout(int theSocketTimeout) { + mySocketTimeout = theSocketTimeout; + myHttpClient=null; + } + + @SuppressWarnings("unchecked") + private T instantiateProxy(Class theClientType, InvocationHandler theInvocationHandler) { + T proxy = (T) Proxy.newProxyInstance(theClientType.getClassLoader(), new Class[] { theClientType }, theInvocationHandler); + return proxy; } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java index 47e149482fb..ec9137b15af 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java @@ -260,12 +260,15 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin b.append('/'); b.append(getResourceName()); b.append('/'); - b.append(response.getId().getValue()); - if (response.getVersionId() != null && response.getVersionId().isEmpty() == false) { - b.append("/_history/"); + b.append(response.getId().getIdPart()); + if (response.getId().hasVersionIdPart()) { + b.append("/"+Constants.PARAM_HISTORY+"/"); + b.append(response.getId().getVersionIdPart()); + }else if (response.getVersionId() != null && response.getVersionId().isEmpty() == false) { + b.append("/"+Constants.PARAM_HISTORY+"/"); b.append(response.getVersionId().getValue()); } - theResponse.addHeader("Location", b.toString()); + theResponse.addHeader(Constants.HEADER_CONTENT_LOCATION, b.toString()); } private static void parseTagValue(TagList theTagList, String theCompleteHeaderValue, StringBuilder theBuffer) { @@ -396,7 +399,7 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin } public static MethodOutcome process2xxResponse(FhirContext theContext, String theResourceName, int theResponseStatusCode, String theResponseMimeType, Reader theResponseReader, Map> theHeaders) { - List locationHeaders = theHeaders.get("location"); + List locationHeaders = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC); MethodOutcome retVal = new MethodOutcome(); if (locationHeaders != null && locationHeaders.size() > 0) { String locationHeader = locationHeaders.get(0); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java index c857f1dc103..90ece9f949d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java @@ -218,10 +218,12 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding 1) { throw new InternalErrorException("Method returned multiple resources"); } - RestfulServer.streamResponseAsResource(theServer, theResponse, result.getResources(0, 1).get(0), responseEncoding, prettyPrint, requestIsBrowser, narrativeMode); + RestfulServer.streamResponseAsResource(theServer, theResponse, result.getResources(0, 1).get(0), responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, respondGzip); break; } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/Request.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/Request.java index db0063bf98a..50ff868f4ad 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/Request.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/Request.java @@ -20,7 +20,6 @@ package ca.uhn.fhir.rest.method; * #L% */ -import java.io.Reader; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -48,6 +47,7 @@ public class Request { private HttpServletResponse myServletResponse; private IdDt myVersion; private Map> myUnqualifiedToQualifiedNames; + private boolean myRespondGzip; public String getCompleteUrl() { return myCompleteUrl; @@ -177,4 +177,12 @@ public class Request { return retVal; } + public void setRespondGzip(boolean theRespondGzip) { + myRespondGzip=theRespondGzip; + } + + public boolean isRespondGzip() { + return myRespondGzip; + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java index f01047a55fb..2457cd7eec1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java @@ -73,18 +73,17 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe @Override protected void addParametersForServerRequest(Request theRequest, Object[] theParams) { /* - * We are being a bit lenient here, since technically the client is - * supposed to include the version in the Content-Location header, but - * we allow it in the PUT URL as well.. + * We are being a bit lenient here, since technically the client is supposed to include the version in the + * Content-Location header, but we allow it in the PUT URL as well.. */ String locationHeader = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_LOCATION); IdDt id = new IdDt(locationHeader); if (isNotBlank(id.getResourceType())) { if (!getResourceName().equals(id.getResourceType())) { - throw new InvalidRequestException("Attempting to update '"+getResourceName()+ "' but content-location header specifies different resource type '"+id.getResourceType()+"' - header value: " + locationHeader); + throw new InvalidRequestException("Attempting to update '" + getResourceName() + "' but content-location header specifies different resource type '" + id.getResourceType() + "' - header value: " + locationHeader); } } - + if (isNotBlank(locationHeader)) { MethodOutcome mo = new MethodOutcome(); parseContentLocation(mo, getResourceName(), locationHeader); @@ -126,15 +125,24 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe } public static HttpPutClientInvocation createUpdateInvocation(IResource theResource, IdDt idDt, IdDt versionIdDt, FhirContext context) { - String id = idDt.getValue(); String resourceName = context.getResourceDefinition(theResource).getName(); StringBuilder urlExtension = new StringBuilder(); urlExtension.append(resourceName); urlExtension.append('/'); - urlExtension.append(id); + urlExtension.append(idDt.getIdPart()); HttpPutClientInvocation retVal = new HttpPutClientInvocation(context, theResource, urlExtension.toString()); - if (versionIdDt != null) { + if (idDt.hasVersionIdPart()) { + String versionId = versionIdDt.getValue(); + if (StringUtils.isNotBlank(versionId)) { + StringBuilder b = new StringBuilder(); + b.append('/'); + b.append(urlExtension); + b.append("/_history/"); + b.append(versionId); + retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, b.toString()); + } + } else if (versionIdDt != null) { String versionId = versionIdDt.getValue(); if (StringUtils.isNotBlank(versionId)) { StringBuilder b = new StringBuilder(); @@ -145,7 +153,7 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, b.toString()); } } - + addTagsToPostOrPut(theResource, retVal); return retVal; @@ -158,7 +166,7 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe if (theRequest.getVersionId() == null) { return false; } - }else { + } else { if (theRequest.getVersionId() != null) { return false; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java index 7ec8a4e9f2d..caad3ec392f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java @@ -45,6 +45,7 @@ public class Constants { public static final String HEADER_ACCEPT = "Accept"; public static final String HEADER_CATEGORY = "Category"; public static final String HEADER_CONTENT_LOCATION = "Content-Location"; + public static final String HEADER_CONTENT_LOCATION_LC = HEADER_CONTENT_LOCATION.toLowerCase(); public static final String HEADER_CONTENT_TYPE = "Content-Type"; public static final String HEADER_LAST_MODIFIED = "Last-Modified"; public static final String HEADER_LAST_MODIFIED_LOWERCASE = HEADER_LAST_MODIFIED.toLowerCase(); @@ -81,6 +82,9 @@ public class Constants { public static final int STATUS_HTTP_422_UNPROCESSABLE_ENTITY = 422; public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500; public static final String URL_TOKEN_HISTORY = "_history"; + public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; + public static final String HEADER_CONTENT_ENCODING = "Content-Encoding"; + public static final String ENCODING_GZIP = "gzip"; static { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 0822e9d4b39..c117e479ce5 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -23,8 +23,10 @@ package ca.uhn.fhir.rest.server; import static org.apache.commons.lang3.StringUtils.*; import java.io.IOException; +import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; +import java.io.Writer; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URLEncoder; @@ -36,6 +38,7 @@ import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.UUID; +import java.util.zip.GZIPOutputStream; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; @@ -352,7 +355,8 @@ public class RestfulServer extends HttpServlet { boolean prettyPrint = prettyPrintResponse(theRequest); boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest()); NarrativeModeEnum narrativeMode = determineNarrativeMode(theRequest); - streamResponseAsBundle(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start, count, thePagingAction); + boolean respondGzip=theRequest.isRespondGzip(); + streamResponseAsBundle(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start, count, thePagingAction, respondGzip); } @@ -488,6 +492,17 @@ public class RestfulServer extends HttpServlet { // TODO: look for more tokens for version, compartments, etc... + String acceptEncoding = theRequest.getHeader(Constants.HEADER_ACCEPT_ENCODING); + boolean respondGzip=false; + if (acceptEncoding != null) { + String[] parts = acceptEncoding.trim().split("\\s*,\\s*"); + for (String string : parts) { + if (string.equals("gzip")) { + respondGzip=true; + } + } + } + Request r = new Request(); r.setResourceName(resourceName); r.setId(id); @@ -500,6 +515,7 @@ public class RestfulServer extends HttpServlet { r.setCompleteUrl(completeUrl); r.setServletRequest(theRequest); r.setServletResponse(theResponse); + r.setRespondGzip(respondGzip); String pagingAction = theRequest.getParameter(Constants.PARAM_PAGINGACTION); if (getPagingProvider() != null && isNotBlank(pagingAction)) { @@ -526,10 +542,14 @@ public class RestfulServer extends HttpServlet { theResponse.setContentType("text/plain"); theResponse.setCharacterEncoding("UTF-8"); theResponse.getWriter().write(e.getMessage()); - } catch (BaseServerResponseException e) { + } catch (Throwable e) { + int statusCode = 500; if (e instanceof InternalErrorException) { ourLog.error("Failure during REST processing", e); + } else if (e instanceof BaseServerResponseException) { + ourLog.warn("Failure during REST processing: {}", e.toString()); + statusCode=((BaseServerResponseException) e).getStatusCode(); } else { ourLog.warn("Failure during REST processing: {}", e.toString()); } @@ -539,19 +559,15 @@ public class RestfulServer extends HttpServlet { issue.getSeverity().setValueAsEnum(IssueSeverityEnum.ERROR); issue.getDetails().setValue(e.toString() + "\n\n" + ExceptionUtils.getStackTrace(e)); - streamResponseAsResource(this, theResponse, oo, determineResponseEncoding(theRequest), true, false, NarrativeModeEnum.NORMAL, e.getStatusCode()); - - theResponse.setStatus(e.getStatusCode()); + streamResponseAsResource(this, theResponse, oo, determineResponseEncoding(theRequest), true, false, NarrativeModeEnum.NORMAL, statusCode,false); + + theResponse.setStatus(statusCode); addHeadersToResponse(theResponse); theResponse.setContentType("text/plain"); theResponse.setCharacterEncoding("UTF-8"); theResponse.getWriter().append(e.getMessage()); theResponse.getWriter().close(); - } catch (Throwable t) { - // TODO: handle this better - ourLog.error("Failed to process invocation", t); - throw new ServletException(t); } } @@ -666,7 +682,6 @@ public class RestfulServer extends HttpServlet { myPlainProviders = theProviders; } - /** * Sets the non-resource specific providers which implement method calls on this server. * @@ -685,7 +700,6 @@ public class RestfulServer extends HttpServlet { myPlainProviders = Arrays.asList(theProviders); } - /** * Sets the resource providers for this server */ @@ -769,7 +783,7 @@ public class RestfulServer extends HttpServlet { return bundle; } - public static String createPagingLink(String theServerBase, String theSearchId, int theOffset, int theCount) { + public static String createPagingLink(String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) { StringBuilder b = new StringBuilder(); b.append(theServerBase); b.append('?'); @@ -788,6 +802,16 @@ public class RestfulServer extends HttpServlet { b.append(Constants.PARAM_COUNT); b.append('='); b.append(theCount); + b.append('&'); + b.append(Constants.PARAM_FORMAT); + b.append('='); + b.append(theResponseEncoding.getRequestContentType()); + if (thePrettyPrint) { + b.append('&'); + b.append(Constants.PARAM_PRETTY); + b.append('='); + b.append(Constants.PARAM_PRETTY_VALUE_TRUE); + } return b.toString(); } @@ -911,7 +935,7 @@ public class RestfulServer extends HttpServlet { } public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser, - NarrativeModeEnum theNarrativeMode, int theOffset, Integer theLimit, String theSearchId) throws IOException { + NarrativeModeEnum theNarrativeMode, int theOffset, Integer theLimit, String theSearchId, boolean theRespondGzip) throws IOException { assert !theServerBase.endsWith("/"); theHttpResponse.setStatus(200); @@ -964,18 +988,24 @@ public class RestfulServer extends HttpServlet { Bundle bundle = createBundleFromResourceList(theServer.getFhirContext(), theServer.getServerName(), resourceList, theServerBase, theCompleteUrl, theResult.size()); bundle.setPublished(theResult.getPublished()); - - if (searchId != null) { - if (theOffset + numToReturn < theResult.size()) { - bundle.getLinkNext().setValue(createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn)); - } - if (theOffset > 0) { - int start = Math.max(0, theOffset - numToReturn); - bundle.getLinkPrevious().setValue(createPagingLink(theServerBase, searchId, start, numToReturn)); + + if (theServer.getPagingProvider() != null) { + int limit; + limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize(); + limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize()); + + if (searchId != null) { + if (theOffset + numToReturn < theResult.size()) { + bundle.getLinkNext().setValue(createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint)); + } + if (theOffset > 0) { + int start = Math.max(0, theOffset - limit); + bundle.getLinkPrevious().setValue(createPagingLink(theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint)); + } } } - PrintWriter writer = theHttpResponse.getWriter(); + Writer writer = getWriter(theHttpResponse, theRespondGzip); try { if (theNarrativeMode == NarrativeModeEnum.ONLY) { for (IResource next : resourceList) { @@ -990,13 +1020,24 @@ public class RestfulServer extends HttpServlet { } } - public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException { - int stausCode = 200; - streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode); + private static Writer getWriter(HttpServletResponse theHttpResponse, boolean theRespondGzip) throws UnsupportedEncodingException, IOException { + Writer writer; + if (theRespondGzip) { + theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP); + writer = new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), "UTF-8"); + }else { + writer = theHttpResponse.getWriter(); + } + return writer; } - private static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, - boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int stausCode) throws IOException { + public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, boolean theRespondGzip) throws IOException { + int stausCode = 200; + streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip); + } + + private static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip) + throws IOException { theHttpResponse.setStatus(stausCode); if (theResource instanceof Binary) { @@ -1041,7 +1082,7 @@ public class RestfulServer extends HttpServlet { } } - PrintWriter writer = theHttpResponse.getWriter(); + Writer writer = getWriter(theHttpResponse, theRespondGzip); try { if (theNarrativeMode == NarrativeModeEnum.ONLY) { writer.append(theResource.getText().getDiv().getValueAsString()); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java index 726843d2157..00b0853fbcb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java @@ -46,6 +46,7 @@ public abstract class BaseServerResponseException extends RuntimeException { registerExceptionType(ResourceVersionNotSpecifiedException.STATUS_CODE, ResourceVersionNotSpecifiedException.class); registerExceptionType(ResourceVersionConflictException.STATUS_CODE, ResourceVersionConflictException.class); registerExceptionType(UnprocessableEntityException.STATUS_CODE, UnprocessableEntityException.class); + registerExceptionType(ResourceGoneException.STATUS_CODE, ResourceGoneException.class); } private final OperationOutcome myOperationOutcome; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceGoneException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceGoneException.java new file mode 100644 index 00000000000..764b70883bd --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceGoneException.java @@ -0,0 +1,54 @@ +package ca.uhn.fhir.rest.server.exceptions; + +/* + * #%L + * HAPI FHIR Library + * %% + * Copyright (C) 2014 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.dstu.composite.IdentifierDt; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.Constants; + +/** + * Represents an HTTP 410 Resource Gone response, which gvenerally + * indicates that the resource has been deleted + */ +public class ResourceGoneException extends BaseServerResponseException { + + public static final int STATUS_CODE = Constants.STATUS_HTTP_410_GONE; + + public ResourceGoneException(IdDt theId) { + super(STATUS_CODE, "Resource " + (theId != null ? theId.getValue() : "") + " is gone/deleted"); + } + + public ResourceGoneException(Class theClass, IdentifierDt thePatientId) { + super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + thePatientId + " is gone/deleted"); + } + + public ResourceGoneException(Class theClass, IdDt thePatientId) { + super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + thePatientId + " is gone/deleted"); + } + + public ResourceGoneException(String theMessage) { + super(STATUS_CODE, theMessage); + } + + private static final long serialVersionUID = 1L; + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/RestfulServerTesterServlet.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/RestfulServerTesterServlet.java deleted file mode 100644 index b856f78fd1b..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/RestfulServerTesterServlet.java +++ /dev/null @@ -1,508 +0,0 @@ -package ca.uhn.fhir.rest.server.tester; - -/* - * #%L - * HAPI FHIR Library - * %% - * Copyright (C) 2014 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import java.io.IOException; -import java.io.InputStream; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.output.WriterOutputStream; -import org.apache.commons.lang3.StringEscapeUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.entity.ContentType; -import org.thymeleaf.TemplateEngine; -import org.thymeleaf.TemplateProcessingParameters; -import org.thymeleaf.context.WebContext; -import org.thymeleaf.resourceresolver.IResourceResolver; -import org.thymeleaf.standard.StandardDialect; -import org.thymeleaf.templateresolver.TemplateResolver; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.RuntimeResourceDefinition; -import ca.uhn.fhir.model.api.IQueryParameterType; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.dstu.composite.IdentifierDt; -import ca.uhn.fhir.model.dstu.resource.Conformance; -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.model.primitive.StringDt; -import ca.uhn.fhir.parser.DataFormatException; -import ca.uhn.fhir.rest.annotation.Metadata; -import ca.uhn.fhir.rest.client.GenericClient; -import ca.uhn.fhir.rest.client.api.IBasicClient; -import ca.uhn.fhir.rest.server.Constants; -import ca.uhn.fhir.rest.server.EncodingEnum; - -public class RestfulServerTesterServlet extends HttpServlet { - - private static final boolean DEBUGMODE = true; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerTesterServlet.class); - private static final String PUBLIC_TESTER_RESULT_HTML = "/PublicTesterResult.html"; - private static final long serialVersionUID = 1L; - private FhirContext myCtx; - private String myServerBase; - private HashMap myStaticResources; - - private TemplateEngine myTemplateEngine; - private Set myFilterHeaders; - - public RestfulServerTesterServlet() { - myStaticResources = new HashMap(); - myStaticResources.put("jquery-2.1.0.min.js", "text/javascript"); - myStaticResources.put("PublicTester.js", "text/javascript"); - myStaticResources.put("PublicTester.css", "text/css"); - myStaticResources.put("hapi_fhir_banner.png", "image/png"); - myStaticResources.put("hapi_fhir_banner_right.png", "image/png"); - myStaticResources.put("shCore.js", "text/javascript"); - myStaticResources.put("shBrushJScript.js", "text/javascript"); - myStaticResources.put("shBrushXml.js", "text/javascript"); - myStaticResources.put("shBrushPlain.js", "text/javascript"); - myStaticResources.put("shCore.css", "text/css"); - myStaticResources.put("shThemeDefault.css", "text/css"); - myStaticResources.put("json2.js", "text/javascript"); - myStaticResources.put("minify.json.js", "text/javascript"); - - myCtx = new FhirContext(); - } - - public FhirContext getFhirContext() { - return myCtx; - } - - @Override - public void init(ServletConfig theConfig) throws ServletException { - myTemplateEngine = new TemplateEngine(); - TemplateResolver resolver = new TemplateResolver(); - resolver.setResourceResolver(new ProfileResourceResolver()); - myTemplateEngine.setTemplateResolver(resolver); - StandardDialect dialect = new StandardDialect(); - myTemplateEngine.setDialect(dialect); - myTemplateEngine.initialize(); - } - - public void setServerBase(String theServerBase) { - myServerBase = theServerBase; - } - - private RuntimeResourceDefinition getResourceType(HttpServletRequest theReq) throws ServletException { - String resourceName = StringUtils.defaultString(theReq.getParameter("resourceName")); - RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName); - if (def == null) { - throw new ServletException("Invalid resourceName: " + resourceName); - } - return def; - } - - private void streamResponse(String theResourceName, String theContentType, HttpServletResponse theResp) throws IOException { - InputStream res = RestfulServerTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/" + theResourceName); - theResp.setContentType(theContentType); - IOUtils.copy(res, theResp.getOutputStream()); - } - - @Override - protected void doGet(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException { - if (DEBUGMODE) { - myTemplateEngine.getCacheManager().clearAllCaches(); - } - - try { - ourLog.info("RequestURI: {}", theReq.getPathInfo()); - - String resName = theReq.getPathInfo().substring(1); - if (myStaticResources.containsKey(resName)) { - streamResponse(resName, myStaticResources.get(resName), theResp); - return; - } - - ConformanceClient client = myCtx.newRestfulClient(ConformanceClient.class, myServerBase); - Conformance conformance = client.getConformance(); - - WebContext ctx = new WebContext(theReq, theResp, theReq.getServletContext(), theReq.getLocale()); - ctx.setVariable("conf", conformance); - ctx.setVariable("base", myServerBase); - ctx.setVariable("jsonEncodedConf", myCtx.newJsonParser().encodeResourceToString(conformance)); - addStandardVariables(ctx, theReq.getParameterMap()); - - theResp.setContentType("text/html"); - theResp.setCharacterEncoding("UTF-8"); - - myTemplateEngine.process(theReq.getPathInfo(), ctx, theResp.getWriter()); - } catch (Exception e) { - ourLog.error("Failed to respond", e); - theResp.sendError(500, e.getMessage()); - } - } - - private void addStandardVariables(WebContext theCtx, Map theParameterMap) { - addStandardVariable(theCtx, theParameterMap, "configEncoding"); - addStandardVariable(theCtx, theParameterMap, "configPretty"); - } - - private void addStandardVariable(WebContext theCtx, Map theParameterMap, String key) { - if (theParameterMap.containsKey(key) && theParameterMap.get(key).length > 0) { - theCtx.setVariable(key, theParameterMap.get(key)[0]); - } - } - - @Override - protected void doPost(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException { - if (DEBUGMODE) { - myTemplateEngine.getCacheManager().clearAllCaches(); - } - - GenericClient client = (GenericClient) myCtx.newRestfulGenericClient(myServerBase); - client.setKeepResponses(true); - boolean returnsResource; - long latency=0; - - try { - String method = theReq.getParameter("method"); - - String prettyParam = theReq.getParameter("configPretty"); - if ("on".equals(prettyParam)) { - client.setPrettyPrint(true); - } - if ("xml".equals(theReq.getParameter("configEncoding"))) { - client.setEncoding(EncodingEnum.XML); - } else if ("json".equals(theReq.getParameter("configEncoding"))) { - client.setEncoding(EncodingEnum.JSON); - } - - long start = System.currentTimeMillis(); - if ("conformance".equals(method)) { - returnsResource = true; - client.conformance(); - } else if ("read".equals(method)) { - RuntimeResourceDefinition def = getResourceType(theReq); - String id = StringUtils.defaultString(theReq.getParameter("id")); - if (StringUtils.isBlank(id)) { - theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified"); - } - returnsResource = true; - - client.read(def.getImplementingClass(), new IdDt(id)); - - } else if ("vread".equals(method)) { - RuntimeResourceDefinition def = getResourceType(theReq); - String id = StringUtils.defaultString(theReq.getParameter("id")); - if (StringUtils.isBlank(id)) { - theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified"); - } - - String versionId = StringUtils.defaultString(theReq.getParameter("versionid")); - if (StringUtils.isBlank(versionId)) { - theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No Version ID specified"); - } - returnsResource = true; - - client.vread(def.getImplementingClass(), new IdDt(id), new IdDt(versionId)); - - } else if ("delete".equals(method)) { - RuntimeResourceDefinition def = getResourceType(theReq); - String id = StringUtils.defaultString(theReq.getParameter("id")); - if (StringUtils.isBlank(id)) { - theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified"); - } - - returnsResource = false; - - client.delete(def.getImplementingClass(), new IdDt(id)); - - } else if ("history-instance".equals(method) || "history-server".equals(method) || "history-type".equals(method)) { - RuntimeResourceDefinition def = getResourceType(theReq); - String id = StringUtils.defaultString(theReq.getParameter("id")); - if (StringUtils.isBlank(id)) { - theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified"); - } - - returnsResource = false; - - client.history(def.getImplementingClass(), new IdDt(id),null,null); - - } else if ("create".equals(method)) { - IResource resource = parseIncomingResource(theReq, theResp, client); - returnsResource = false; - - client.create(resource); - - } else if ("validate".equals(method)) { - IResource resource = parseIncomingResource(theReq, theResp, client); - returnsResource = false; - - client.validate(resource); - - } else if ("update".equals(method)) { - String id = StringUtils.defaultString(theReq.getParameter("id")); - if (StringUtils.isBlank(id)) { - theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified"); - } - - IResource resource = parseIncomingResource(theReq, theResp, client); - returnsResource = false; - - client.update(new IdDt(id), resource); - - } else if ("searchType".equals(method)) { - Map> params = new HashMap>(); - - HashSet hashSet = new HashSet(theReq.getParameterMap().keySet()); - String paramName = null; - IQueryParameterType paramValue = null; - while (hashSet.isEmpty() == false) { - - String nextKey = hashSet.iterator().next(); - String nextValue = theReq.getParameter(nextKey); - paramName = null; - paramValue = null; - - if (nextKey.startsWith("param.token.")) { - int prefixLength = "param.token.".length(); - paramName = nextKey.substring(prefixLength + 2); - String systemKey = "param.token." + "1." + paramName; - String valueKey = "param.token." + "2." + paramName; - String system = theReq.getParameter(systemKey); - String value = theReq.getParameter(valueKey); - paramValue = new IdentifierDt(system, value); - hashSet.remove(systemKey); - hashSet.remove(valueKey); - } else if (nextKey.startsWith("param.string.")) { - paramName = nextKey.substring("param.string.".length()); - paramValue = new StringDt(nextValue); - } - - if (paramName != null) { - if (params.containsKey(paramName) == false) { - params.put(paramName, new ArrayList()); - } - params.get(paramName).add(paramValue); - } - - hashSet.remove(nextKey); - } - - RuntimeResourceDefinition def = getResourceType(theReq); - - returnsResource = false; - client.search(def.getImplementingClass(), params); - - } else { - theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "Invalid method: " + method); - return; - } - - latency = System.currentTimeMillis() - start; - } catch (DataFormatException e) { - ourLog.error("Failed to invoke method", e); - returnsResource = false; - } catch (Exception e) { - ourLog.error("Failure during processing", e); - returnsResource = false; - } - - try { - HttpRequestBase lastRequest = client.getLastRequest(); - String requestBody = null; - String requestSyntaxHighlighterClass = null; - - if (lastRequest instanceof HttpEntityEnclosingRequest) { - HttpEntityEnclosingRequest lastEERequest = (HttpEntityEnclosingRequest) lastRequest; - HttpEntity lastEE = lastEERequest.getEntity(); - if (lastEE.isRepeatable()) { - StringWriter requestCapture = new StringWriter(); - lastEE.writeTo(new WriterOutputStream(requestCapture, "UTF-8")); - requestBody = requestCapture.toString(); - ContentType ct = ContentType.get(lastEE); - String mimeType = ct.getMimeType(); - EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType); - if (ctEnum == null) { - requestSyntaxHighlighterClass = "brush: plain"; - } else { - switch (ctEnum) { - case JSON: - requestSyntaxHighlighterClass = "brush: jscript"; - break; - case XML: - default: - requestSyntaxHighlighterClass = "brush: xml"; - break; - } - } - } - } - String resultSyntaxHighlighterClass; - String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null; - String action = client.getLastRequest() != null ? client.getLastRequest().getMethod() : null; - String resultStatus = client.getLastResponse() != null ? client.getLastResponse().getStatusLine().toString() : null; - String resultBody = client.getLastResponseBody(); - - HttpResponse lastResponse = client.getLastResponse(); - ContentType ct = lastResponse != null ? ContentType.get(lastResponse.getEntity()) : null; - String mimeType = ct != null ? ct.getMimeType() : null; - EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType); - String narrativeString = ""; - - if (ctEnum == null) { - resultSyntaxHighlighterClass = "brush: plain"; - } else { - switch (ctEnum) { - case JSON: - resultSyntaxHighlighterClass = "brush: jscript"; - if (returnsResource) { - narrativeString = parseNarrative(ctEnum, resultBody); - } - break; - case XML: - default: - resultSyntaxHighlighterClass = "brush: xml"; - if (returnsResource) { - narrativeString = parseNarrative(ctEnum, resultBody); - } - break; - } - } - - Header[] requestHeaders = lastRequest != null ? applyHeaderFilters(lastRequest.getAllHeaders()) : new Header[0]; - Header[] responseHeaders = lastResponse != null ? applyHeaderFilters(lastResponse.getAllHeaders()) : new Header[0]; - - WebContext ctx = new WebContext(theReq, theResp, theReq.getServletContext(), theReq.getLocale()); - ctx.setVariable("base", myServerBase); - ctx.setVariable("requestUrl", requestUrl); - ctx.setVariable("action", action); - ctx.setVariable("resultStatus", resultStatus); - ctx.setVariable("requestBody", StringEscapeUtils.escapeHtml4(requestBody)); - ctx.setVariable("requestSyntaxHighlighterClass", requestSyntaxHighlighterClass); - ctx.setVariable("resultBody", StringEscapeUtils.escapeHtml4(resultBody)); - ctx.setVariable("resultSyntaxHighlighterClass", resultSyntaxHighlighterClass); - ctx.setVariable("requestHeaders", requestHeaders); - ctx.setVariable("responseHeaders", responseHeaders); - ctx.setVariable("narrative", narrativeString); - ctx.setVariable("latencyMs", latency); - - myTemplateEngine.process(PUBLIC_TESTER_RESULT_HTML, ctx, theResp.getWriter()); - } catch (Exception e) { - ourLog.error("Failure during processing", e); - theResp.sendError(500, e.toString()); - } - } - - private IResource parseIncomingResource(HttpServletRequest theReq, HttpServletResponse theResp, GenericClient theClient) throws ServletException, IOException { - RuntimeResourceDefinition def = getResourceType(theReq); - String resourceText = StringUtils.defaultString(theReq.getParameter("resource")); - if (StringUtils.isBlank(resourceText)) { - theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No resource content specified"); - } - - IResource resource; - if (theClient.getEncoding() == null) { - if (resourceText.trim().startsWith("{")) { - resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText); - } else { - resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText); - } - } else if (theClient.getEncoding() == EncodingEnum.XML) { - resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText); - } else { - resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText); - } - return resource; - } - - private Header[] applyHeaderFilters(Header[] theAllHeaders) { - if (myFilterHeaders == null || myFilterHeaders.isEmpty()) { - return theAllHeaders; - } - ArrayList

retVal = new ArrayList
(); - for (Header next : theAllHeaders) { - if (!myFilterHeaders.contains(next.getName().toLowerCase())) { - retVal.add(next); - } - } - return retVal.toArray(new Header[retVal.size()]); - } - - /** - * If set, the headers named here will be stripped from requests/responses before they are displayed to the user. - * This can be used, for instance, to filter out "Authorization" headers. Note that names are not case sensitive. - */ - public void setFilterHeaders(String... theHeaderNames) { - myFilterHeaders = new HashSet(); - if (theHeaderNames != null) { - for (String next : theHeaderNames) { - myFilterHeaders.add(next.toLowerCase()); - } - } - } - - private String parseNarrative(EncodingEnum theCtEnum, String theResultBody) { - try { - IResource resource = theCtEnum.newParser(myCtx).parseResource(theResultBody); - String retVal = resource.getText().getDiv().getValueAsString(); - return StringUtils.defaultString(retVal); - } catch (Exception e) { - ourLog.error("Failed to parse resource", e); - return ""; - } - } - - private interface ConformanceClient extends IBasicClient { - @Metadata - Conformance getConformance(); - } - - private final class ProfileResourceResolver implements IResourceResolver { - - @Override - public String getName() { - return getClass().getCanonicalName(); - } - - @Override - public InputStream getResourceAsStream(TemplateProcessingParameters theTemplateProcessingParameters, String theName) { - ourLog.debug("Loading template: {}", theName); - if ("/".equals(theName)) { - return RestfulServerTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTester.html"); - } - if (PUBLIC_TESTER_RESULT_HTML.equals(theName)) { - return RestfulServerTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTesterResult.html"); - } - - return null; - } - } - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/RestfulTesterServlet.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/RestfulTesterServlet.java deleted file mode 100644 index ee47796ee4d..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/RestfulTesterServlet.java +++ /dev/null @@ -1,899 +0,0 @@ -package ca.uhn.fhir.rest.server.tester; - -/* - * #%L - * HAPI FHIR Library - * %% - * Copyright (C) 2014 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import static org.apache.commons.lang3.StringUtils.defaultIfEmpty; -import static org.apache.commons.lang3.StringUtils.defaultString; -import static org.apache.commons.lang3.StringUtils.isBlank; -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringEscapeUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.entity.ContentType; -import org.thymeleaf.TemplateEngine; -import org.thymeleaf.TemplateProcessingParameters; -import org.thymeleaf.context.WebContext; -import org.thymeleaf.resourceresolver.IResourceResolver; -import org.thymeleaf.standard.StandardDialect; -import org.thymeleaf.templateresolver.TemplateResolver; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.RuntimeResourceDefinition; -import ca.uhn.fhir.context.RuntimeSearchParam; -import ca.uhn.fhir.model.api.Bundle; -import ca.uhn.fhir.model.api.ExtensionDt; -import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.api.Include; -import ca.uhn.fhir.model.dstu.resource.Conformance; -import ca.uhn.fhir.model.dstu.resource.Conformance.Rest; -import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource; -import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum; -import ca.uhn.fhir.model.primitive.DateTimeDt; -import ca.uhn.fhir.model.primitive.DecimalDt; -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.model.primitive.StringDt; -import ca.uhn.fhir.parser.DataFormatException; -import ca.uhn.fhir.rest.client.GenericClient; -import ca.uhn.fhir.rest.client.IGenericClient; -import ca.uhn.fhir.rest.gclient.IQuery; -import ca.uhn.fhir.rest.gclient.IUntypedQuery; -import ca.uhn.fhir.rest.gclient.StringParam; -import ca.uhn.fhir.rest.server.Constants; -import ca.uhn.fhir.rest.server.EncodingEnum; -import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; - -public class RestfulTesterServlet extends HttpServlet { - - private static final String PARAM_RESOURCE = "resource"; - - private static final String RESOURCE_COUNT_EXT_URL = "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount"; - - private static final boolean DEBUGMODE = true; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulTesterServlet.class); - private static final String PUBLIC_TESTER_RESULT_HTML = "/PublicTesterResult.html"; - private static final long serialVersionUID = 1L; - private FhirContext myCtx; - private LinkedHashMap myIdToServerName = new LinkedHashMap(); - private LinkedHashMap myIdToServerBase = new LinkedHashMap(); - private HashMap myStaticResources; - - private TemplateEngine myTemplateEngine; - private Set myFilterHeaders; - - public RestfulTesterServlet() { - myStaticResources = new HashMap(); - myStaticResources.put("jquery-2.1.0.min.js", "text/javascript"); - myStaticResources.put("PublicTester.js", "text/javascript"); - myStaticResources.put("PublicTester.css", "text/css"); - myStaticResources.put("hapi_fhir_banner.png", "image/png"); - myStaticResources.put("hapi_fhir_banner_right.png", "image/png"); - myStaticResources.put("shCore.js", "text/javascript"); - myStaticResources.put("shBrushJScript.js", "text/javascript"); - myStaticResources.put("shBrushXml.js", "text/javascript"); - myStaticResources.put("shBrushPlain.js", "text/javascript"); - myStaticResources.put("shCore.css", "text/css"); - myStaticResources.put("shThemeDefault.css", "text/css"); - myStaticResources.put("json2.js", "text/javascript"); - myStaticResources.put("minify.json.js", "text/javascript"); - - myStaticResources.put("css/bootstrap.min.css", "text/css"); - myStaticResources.put("css/tester.css", "text/css"); - myStaticResources.put("img/hapi_fhir_banner.png", "image/png"); - myStaticResources.put("img/hapi_fhir_banner_right.png", "image/png"); - myStaticResources.put("js/RestfulTester.js", "text/javascript"); - - myStaticResources.put("js/bootstrap.min.js", "text/javascript"); - myStaticResources.put("js/jquery-2.1.0.min.js", "text/javascript"); - - myStaticResources.put("css/bootstrap-datetimepicker.min.css", "text/css"); - myStaticResources.put("js/bootstrap-datetimepicker.min.js", "text/javascript"); - myStaticResources.put("js/moment.min.js", "text/javascript"); - - myStaticResources.put("js/select2.min.js", "text/javascript"); - myStaticResources.put("css/select2.css", "text/css"); - myStaticResources.put("css/select2.png", "image/png"); - myStaticResources.put("css/select2x2.png", "image/png"); - myStaticResources.put("css/select2-spinner.gif", "image/gif"); - - myStaticResources.put("fonts/glyphicons-halflings-regular.eot", "application/octet-stream"); - myStaticResources.put("fonts/glyphicons-halflings-regular.svg", "application/octet-stream"); - myStaticResources.put("fonts/glyphicons-halflings-regular.ttf", "application/octet-stream"); - myStaticResources.put("fonts/glyphicons-halflings-regular.woff", "application/octet-stream"); - - myStaticResources.put("fa/css/font-awesome.css", "text/css"); - myStaticResources.put("fa/css/font-awesome.min.css", "text/css"); - myStaticResources.put("fa/fonts/fontawesome-webfont.eot", "application/octet-stream"); - myStaticResources.put("fa/fonts/fontawesome-webfont.svg", "application/octet-stream"); - myStaticResources.put("fa/fonts/fontawesome-webfont.ttf", "application/octet-stream"); - myStaticResources.put("fa/fonts/fontawesome-webfont.woff", "application/octet-stream"); - myStaticResources.put("fa/fonts/FontAwesome.otf", "application/octet-stream"); - myStaticResources.put("fa/less/bordered-pulled.less", "text/css"); - myStaticResources.put("fa/less/core.less", "text/css"); - myStaticResources.put("fa/less/fixed-width.less", "text/css"); - myStaticResources.put("fa/less/font-awesome.less", "text/css"); - myStaticResources.put("fa/less/icons.less", "text/css"); - myStaticResources.put("fa/less/larger.less", "text/css"); - myStaticResources.put("fa/less/list.less", "text/css"); - myStaticResources.put("fa/less/mixins.less", "text/css"); - myStaticResources.put("fa/less/path.less", "text/css"); - myStaticResources.put("fa/less/rotated-flipped.less", "text/css"); - myStaticResources.put("fa/less/spinning.less", "text/css"); - myStaticResources.put("fa/less/stacked.less", "text/css"); - myStaticResources.put("fa/less/variables.less", "text/css"); - myStaticResources.put("fa/scss/_bordered-pulled.scss", "text/css"); - myStaticResources.put("fa/scss/_core.scss", "text/css"); - myStaticResources.put("fa/scss/_fixed-width.scss", "text/css"); - myStaticResources.put("fa/scss/_icons.scss", "text/css"); - myStaticResources.put("fa/scss/_larger.scss", "text/css"); - myStaticResources.put("fa/scss/_list.scss", "text/css"); - myStaticResources.put("fa/scss/_mixins.scss", "text/css"); - myStaticResources.put("fa/scss/_path.scss", "text/css"); - myStaticResources.put("fa/scss/_rotated-flipped.scss", "text/css"); - myStaticResources.put("fa/scss/_spinning.scss", "text/css"); - myStaticResources.put("fa/scss/_stacked.scss", "text/css"); - myStaticResources.put("fa/scss/_variables.scss", "text/css"); - myStaticResources.put("fa/scss/font-awesome.scss", "text/css"); - - myCtx = new FhirContext(); - } - - public FhirContext getFhirContext() { - return myCtx; - } - - @Override - public void init(ServletConfig theConfig) throws ServletException { - myTemplateEngine = new TemplateEngine(); - TemplateResolver resolver = new TemplateResolver(); - resolver.setResourceResolver(new ProfileResourceResolver()); - myTemplateEngine.setTemplateResolver(resolver); - StandardDialect dialect = new StandardDialect(); - myTemplateEngine.setDialect(dialect); - myTemplateEngine.initialize(); - } - - public void addServerBase(String theId, String theDisplayName, String theServerBase) { - Validate.notBlank(theId, "theId can not be blank"); - Validate.notBlank(theDisplayName, "theDisplayName can not be blank"); - Validate.notBlank(theServerBase, "theServerBase can not be blank"); - myIdToServerBase.put(theId, theServerBase); - myIdToServerName.put(theId, theDisplayName); - } - - private RuntimeResourceDefinition getResourceType(HttpServletRequest theReq) throws ServletException { - String resourceName = StringUtils.defaultString(theReq.getParameter(PARAM_RESOURCE)); - RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName); - if (def == null) { - throw new ServletException("Invalid resourceName: " + resourceName); - } - return def; - } - - private void streamResponse(String theResourceName, String theContentType, HttpServletResponse theResp) throws IOException { - InputStream res = RestfulTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/" + theResourceName); - theResp.setContentType(theContentType); - IOUtils.copy(res, theResp.getOutputStream()); - } - - private enum ResultType { - RESOURCE, BUNDLE, TAGLIST, NONE - } - - private void processAction(HttpServletRequest theReq, WebContext theContext, IGenericClient theClient, String theServerBase) { - - GenericClient client = (GenericClient) theClient; - client.setKeepResponses(true); - ResultType returnsResource; - long latency = 0; - - String outcomeDescription = null; - try { - String method = theReq.getParameter("action"); - - String prettyParam = theReq.getParameter("pretty"); - if ("true".equals(prettyParam)) { - client.setPrettyPrint(true); - } else if ("false".equals(prettyParam)) { - client.setPrettyPrint(false); - } - EncodingEnum encoding = getRequestEncoding(theReq); - client.setEncoding(encoding); - - long start = System.currentTimeMillis(); - if ("home".equals(method)) { - return; - } else if ("conformance".equals(method)) { - returnsResource = ResultType.RESOURCE; - client.conformance(); - } else if ("read".equals(method)) { - RuntimeResourceDefinition def = getResourceType(theReq); - String id = StringUtils.defaultString(theReq.getParameter("id")); - if (StringUtils.isBlank(id)) { - theContext.getVariables().put("errorMsg", "No ID specified"); - return; - } - returnsResource = ResultType.RESOURCE; - - String versionId = StringUtils.defaultString(theReq.getParameter("vid")); - if (StringUtils.isBlank(versionId)) { - versionId = null; - outcomeDescription = "Read Resource"; - } else { - outcomeDescription = "VRead Resource"; - } - - client.read(def.getImplementingClass(), new IdDt(def.getName(), id, versionId)); - - } else if ("get-tags".equals(method)) { - - Class resType = null; - if (isNotBlank(theReq.getParameter(PARAM_RESOURCE))) { - RuntimeResourceDefinition def = getResourceType(theReq); - resType = def.getImplementingClass(); - String id = theReq.getParameter("resource-tags-id"); - if (isNotBlank(id)) { - String vid = theReq.getParameter("resource-tags-vid"); - if (isNotBlank(vid)) { - client.getTags().forResource(resType, id, vid).execute(); - } else { - client.getTags().forResource(resType, id).execute(); - } - } else { - client.getTags().forResource(resType).execute(); - } - } else { - client.getTags().execute(); - } - returnsResource = ResultType.TAGLIST; - outcomeDescription = "Tag List"; - - } else if ("page".equals(method)) { - - String url = defaultString(theReq.getParameter("page-url")); - if (!url.startsWith(theServerBase)) { - theContext.getVariables().put("errorMsg", "Invalid page URL: " + url); - return; - } - - url = url.replace("&", "&"); - client.loadPage().url(url).execute(); - - returnsResource = ResultType.BUNDLE; - outcomeDescription = "Bundle Page"; - - } else if ("delete".equals(method)) { - RuntimeResourceDefinition def = getResourceType(theReq); - String id = StringUtils.defaultString(theReq.getParameter("resource-delete-id")); - if (StringUtils.isBlank(id)) { - theContext.getVariables().put("errorMsg", "No ID specified"); - return; - } - - returnsResource = ResultType.BUNDLE; - outcomeDescription = "Delete Resource"; - - client.delete(def.getImplementingClass(), new IdDt(id)); - - } else if ("history-resource".equals(method) || "history-server".equals(method)) { - String id = null; - Class type = null; // def.getImplementingClass(); - if (!"history-server".equals(method)) { - RuntimeResourceDefinition def = getResourceType(theReq); - type = def.getImplementingClass(); - id = StringUtils.defaultString(theReq.getParameter("resource-history-id")); - if (StringUtils.isBlank(id)) { - if ("history-instance".equals(method)) { - theContext.getVariables().put("errorMsg", "No ID specified"); - return; - } else { - id = null; - } - } - } - - DateTimeDt since = null; - String sinceStr = theReq.getParameter("since"); - if (isNotBlank(sinceStr)) { - since = new DateTimeDt(sinceStr); - } - - Integer limit = null; - String limitStr = theReq.getParameter("limit"); - if (isNotBlank(limitStr)) { - limit = Integer.parseInt(limitStr); - } - - returnsResource = ResultType.BUNDLE; - outcomeDescription = "Resource History"; - - client.history(type, id, since, limit); - - } else if ("create".equals(method) || "validate".equals(method)) { - boolean validate = "validate".equals(method); - - String body = validate ? theReq.getParameter("resource-validate-body") : theReq.getParameter("resource-create-body"); - if (isBlank(body)) { - theContext.getVariables().put("errorMsg", "No message body specified"); - return; - } - - body = body.trim(); - IResource resource; - try { - if (body.startsWith("{")) { - resource = myCtx.newJsonParser().parseResource(body); - } else if (body.startsWith("<")) { - resource = myCtx.newXmlParser().parseResource(body); - } else { - theContext.getVariables().put("errorMsg", - "Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding)."); - return; - } - } catch (DataFormatException e) { - ourLog.warn("Failed to parse resource", e); - theContext.getVariables().put("errorMsg", "Failed to parse message body. Error was: " + e.getMessage()); - return; - } - - if (validate) { - client.validate(resource); - outcomeDescription = "Validate Resource"; - } else { - String id = theReq.getParameter("resource-create-id"); - if (isNotBlank(id)) { - outcomeDescription = "Update Resource"; - client.update(id, resource); - } else { - outcomeDescription = "Create Resource"; - client.create(resource); - } - } - returnsResource = ResultType.RESOURCE; - - } else if ("search".equals(method)) { - IUntypedQuery search = client.search(); - IQuery query; - if (isNotBlank(theReq.getParameter("resource"))) { - query = search.forResource(getResourceType(theReq).getImplementingClass()); - } else { - query = search.forAllResources(); - } - - outcomeDescription = "Search for Resources"; - - int paramIdx = -1; - while (true) { - paramIdx++; - - String paramIdxString = Integer.toString(paramIdx); - boolean shouldContinue = handleSearchParam(paramIdxString, theReq, query); - if (!shouldContinue) { - break; - } - } - - String[] incValues = theReq.getParameterValues(Constants.PARAM_INCLUDE); - if (incValues != null) { - for (String next : incValues) { - if (isNotBlank(next)) { - query.include(new Include(next)); - } - } - } - - String limit = theReq.getParameter("resource-search-limit"); - if (isNotBlank(limit)) { - if (!limit.matches("[0-9]+")) { - theContext.getVariables().put("errorMsg", "Search limit must be a numeric value."); - return; - } - query.limitTo(Integer.parseInt(limit)); - } - - query.execute(); - returnsResource = ResultType.BUNDLE; - - } else { - theContext.getVariables().put("errorMsg", "Invalid action: " + method); - return; - } - - latency = System.currentTimeMillis() - start; - } catch (DataFormatException e) { - ourLog.error("Failed to invoke method", e); - returnsResource = ResultType.NONE; - } catch (BaseServerResponseException e) { - ourLog.error("Failed to invoke method", e); - returnsResource = ResultType.NONE; - } catch (Exception e) { - ourLog.error("Failure during processing", e); - returnsResource = ResultType.NONE; - } - - try { - HttpRequestBase lastRequest = client.getLastRequest(); - String requestBody = null; - String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null; - String action = client.getLastRequest() != null ? client.getLastRequest().getMethod() : null; - String resultStatus = client.getLastResponse() != null ? client.getLastResponse().getStatusLine().toString() : null; - String resultBody = client.getLastResponseBody(); - - if (lastRequest instanceof HttpEntityEnclosingRequest) { - HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity(); - if (entity.isRepeatable()) { - requestBody = IOUtils.toString(entity.getContent()); - } - } - - HttpResponse lastResponse = client.getLastResponse(); - ContentType ct = lastResponse != null ? ContentType.get(lastResponse.getEntity()) : null; - String mimeType = ct != null ? ct.getMimeType() : null; - EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType); - String narrativeString = ""; - - StringBuilder resultDescription = new StringBuilder(); - Bundle bundle = null; - - if (ctEnum == null) { - resultDescription.append("Non-FHIR response"); - } else { - switch (ctEnum) { - case JSON: - if (returnsResource == ResultType.RESOURCE) { - narrativeString = parseNarrative(ctEnum, resultBody); - resultDescription.append("JSON resource"); - } else if (returnsResource == ResultType.BUNDLE) { - resultDescription.append("JSON bundle"); - bundle = myCtx.newJsonParser().parseBundle(resultBody); - } - break; - case XML: - default: - if (returnsResource == ResultType.RESOURCE) { - narrativeString = parseNarrative(ctEnum, resultBody); - resultDescription.append("XML resource"); - } else if (returnsResource == ResultType.BUNDLE) { - resultDescription.append("XML bundle"); - bundle = myCtx.newXmlParser().parseBundle(resultBody); - } - break; - } - } - - resultDescription.append(" (").append(resultBody.length() + " bytes)"); - - Header[] requestHeaders = lastRequest != null ? applyHeaderFilters(lastRequest.getAllHeaders()) : new Header[0]; - Header[] responseHeaders = lastResponse != null ? applyHeaderFilters(lastResponse.getAllHeaders()) : new Header[0]; - - theContext.setVariable("outcomeDescription", outcomeDescription); - theContext.setVariable("resultDescription", resultDescription.toString()); - theContext.setVariable("action", action); - theContext.setVariable("bundle", bundle); - theContext.setVariable("resultStatus", resultStatus); - theContext.setVariable("requestUrl", requestUrl); - String requestBodyText = format(requestBody, ctEnum); - theContext.setVariable("requestBody", requestBodyText); - String resultBodyText = format(resultBody, ctEnum); - theContext.setVariable("resultBody", resultBodyText); - theContext.setVariable("resultBodyIsLong", resultBodyText.length() > 1000); - theContext.setVariable("requestHeaders", requestHeaders); - theContext.setVariable("responseHeaders", responseHeaders); - theContext.setVariable("narrative", narrativeString); - theContext.setVariable("latencyMs", latency); - - } catch (Exception e) { - ourLog.error("Failure during processing", e); - theContext.getVariables().put("errorMsg", "Error during processing: " + e.getMessage()); - } - } - - private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery theQuery) { - String nextName = theReq.getParameter("param." + paramIdxString + ".name"); - if (isBlank(nextName)) { - return false; - } - - String nextQualifier = StringUtils.defaultString(theReq.getParameter("param." + paramIdxString + ".qualifier")); - - String nextType = theReq.getParameter("param." + paramIdxString + ".type"); - - StringBuilder b = new StringBuilder(); - for (int i = 0; i < 100; i++) { - b.append(defaultString(theReq.getParameter("param." + paramIdxString + "." + i))); - } - - String paramValue = b.toString(); - if (isBlank(paramValue)) { - return true; - } - - if ("token".equals(nextType)) { - if (paramValue.length() < 2) { - return true; - } - } - - // if ("xml".equals(theReq.getParameter("encoding"))) { - // query.encodedXml(); - // }else if ("json".equals(theReq.getParameter("encoding"))) { - // query.encodedJson(); - // } - - theQuery.where(new StringParam(nextName + nextQualifier).matches().value(paramValue)); - - if (StringUtils.isNotBlank(theReq.getParameter("param." + paramIdxString + ".0.name"))) { - handleSearchParam(paramIdxString + ".0", theReq, theQuery); - } - - return true; - } - - private String format(String theResultBody, EncodingEnum theEncodingEnum) { - String str = StringEscapeUtils.escapeHtml4(theResultBody); - if (str == null || theEncodingEnum == null) { - return str; - } - - StringBuilder b = new StringBuilder(); - - if (theEncodingEnum == EncodingEnum.JSON) { - - boolean inValue = false; - boolean inQuote = false; - for (int i = 0; i < str.length(); i++) { - char prevChar = (i > 0) ? str.charAt(i - 1) : ' '; - char nextChar = str.charAt(i); - char nextChar2 = (i + 1) < str.length() ? str.charAt(i + 1) : ' '; - char nextChar3 = (i + 2) < str.length() ? str.charAt(i + 2) : ' '; - char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' '; - char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' '; - char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' '; - if (inQuote) { - b.append(nextChar); - if (prevChar != '\\' && nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') { - b.append("quot;"); - i += 5; - inQuote = false; - } else if (nextChar == '\\' && nextChar2 == '"') { - b.append("quot;"); - i += 5; - inQuote = false; - } - } else { - if (nextChar == ':') { - inValue = true; - b.append(nextChar); - } else if (nextChar == '[' || nextChar == '{') { - b.append(""); - b.append(nextChar); - b.append(""); - inValue = false; - } else if (nextChar == '}' || nextChar == '}' || nextChar == ',') { - b.append(""); - b.append(nextChar); - b.append(""); - inValue = false; - } else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') { - if (inValue) { - b.append("""); - } else { - b.append("""); - } - inQuote = true; - i += 5; - } else if (nextChar == ':') { - b.append(""); - b.append(nextChar); - b.append(""); - inValue = true; - } else { - b.append(nextChar); - } - } - } - - } else { - boolean inQuote = false; - boolean inTag = false; - for (int i = 0; i < str.length(); i++) { - char nextChar = str.charAt(i); - char nextChar2 = (i + 1) < str.length() ? str.charAt(i + 1) : ' '; - char nextChar3 = (i + 2) < str.length() ? str.charAt(i + 2) : ' '; - char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' '; - char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' '; - char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' '; - if (inQuote) { - b.append(nextChar); - if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') { - b.append("quot;"); - i += 5; - inQuote = false; - } - } else if (inTag) { - if (nextChar == '&' && nextChar2 == 'g' && nextChar3 == 't' && nextChar4 == ';') { - b.append(">"); - inTag = false; - i += 3; - } else if (nextChar == ' ') { - b.append(""); - b.append(nextChar); - } else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') { - b.append("""); - inQuote = true; - i += 5; - } else { - b.append(nextChar); - } - } else { - if (nextChar == '&' && nextChar2 == 'l' && nextChar3 == 't' && nextChar4 == ';') { - b.append("<"); - inTag = true; - i += 3; - } else { - b.append(nextChar); - } - } - } - } - - return b.toString(); - } - - private EncodingEnum getRequestEncoding(HttpServletRequest theReq) { - EncodingEnum encoding; - if ("xml".equals(theReq.getParameter("encoding"))) { - encoding = EncodingEnum.XML; - } else if ("json".equals(theReq.getParameter("encoding"))) { - encoding = (EncodingEnum.JSON); - } else { - encoding = null; - } - return encoding; - } - - @Override - protected void doGet(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException { - if (DEBUGMODE) { - myTemplateEngine.getCacheManager().clearAllCaches(); - } - - try { - ourLog.trace("RequestURI: {}", theReq.getPathInfo()); - - String resName = theReq.getPathInfo().substring(1); - if (myStaticResources.containsKey(resName)) { - streamResponse(resName, myStaticResources.get(resName), theResp); - return; - } - - String serverId = theReq.getParameter("server-id"); - String serverBase; - String serverName; - if (isBlank(serverId) && !myIdToServerBase.containsKey(serverId)) { - serverBase = myIdToServerBase.entrySet().iterator().next().getValue(); - serverName = myIdToServerName.entrySet().iterator().next().getValue(); - } else { - serverBase = myIdToServerBase.get(serverId); - serverName = myIdToServerName.get(serverId); - } - - IGenericClient client = myCtx.newRestfulGenericClient(serverBase); - - WebContext ctx = new WebContext(theReq, theResp, theReq.getServletContext(), theReq.getLocale()); - - Conformance conformance; - try { - conformance = client.conformance(); - } catch (Exception e) { - ourLog.warn("Failed to load conformance statement", e); - ctx.getVariables().put("errorMsg", "Failed to load conformance statement, error was: " + e.toString()); - conformance = new Conformance(); - } - - Map resourceCounts = new HashMap(); - long total = 0; - for (Rest nextRest : conformance.getRest()) { - for (RestResource nextResource : nextRest.getResource()) { - List exts = nextResource.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (exts != null && exts.size() > 0) { - Number nextCount = ((DecimalDt) (exts.get(0).getValue())).getValueAsNumber(); - resourceCounts.put(nextResource.getType().getValue(), nextCount); - total += nextCount.longValue(); - } - } - } - if (total > 0) { - for (Rest nextRest : conformance.getRest()) { - Collections.sort(nextRest.getResource(), new Comparator() { - @Override - public int compare(RestResource theO1, RestResource theO2) { - DecimalDt count1 = new DecimalDt(); - List count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (count1exts != null && count1exts.size() > 0) { - count1 = (DecimalDt) count1exts.get(0).getValue(); - } - DecimalDt count2 = new DecimalDt(); - List count2exts = theO2.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL); - if (count2exts != null && count2exts.size() > 0) { - count2 = (DecimalDt) count2exts.get(0).getValue(); - } - int retVal = count2.compareTo(count1); - if (retVal == 0) { - retVal = theO1.getType().getValue().compareTo(theO2.getType().getValue()); - } - return retVal; - } - }); - } - } - - ctx.setVariable("serverId", serverId); - ctx.setVariable("resourceCounts", resourceCounts); - ctx.setVariable("conf", conformance); - ctx.setVariable("base", serverBase); - ctx.setVariable("baseName", serverName); - ctx.setVariable("serverEntries", myIdToServerName.entrySet()); - String resourceName = defaultString(theReq.getParameter(PARAM_RESOURCE)); - ctx.setVariable("resourceName", resourceName); - ctx.setVariable("jsonEncodedConf", myCtx.newJsonParser().encodeResourceToString(conformance)); - addStandardVariables(ctx, theReq.getParameterMap()); - - if (isNotBlank(theReq.getParameter("action"))) { - processAction(theReq, ctx, client, serverBase); - } - - if (isNotBlank(resourceName)) { - RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName); - TreeSet includes = new TreeSet(); - for (Rest nextRest : conformance.getRest()) { - for (RestResource nextRes : nextRest.getResource()) { - if (nextRes.getType().getValue().equals(resourceName)) { - for (StringDt next : nextRes.getSearchInclude()) { - if (next.isEmpty() == false) { - includes.add(next.getValue()); - } - } - } - } - } - ctx.setVariable("includes", includes); - - if (isNotBlank(theReq.getParameter("update-id"))) { - String updateId = theReq.getParameter("update-id"); - String updateVid = defaultIfEmpty(theReq.getParameter("update-vid"), null); - IResource updateResource = client.read(def.getImplementingClass(), new IdDt(resourceName, updateId, updateVid)); - EncodingEnum encoding = getRequestEncoding(theReq); - if (encoding == null) { - encoding = EncodingEnum.XML; - } - String updateResourceString = encoding.newParser(myCtx).setPrettyPrint(true).encodeResourceToString(updateResource); - ctx.setVariable("updateResource", updateResourceString); - ctx.setVariable("updateResourceId", updateId); - } - - } - - theResp.setContentType("text/html"); - theResp.setCharacterEncoding("UTF-8"); - - myTemplateEngine.process(theReq.getPathInfo(), ctx, theResp.getWriter()); - } catch (Exception e) { - ourLog.error("Failed to respond", e); - theResp.sendError(500, e.getMessage()); - } - } - - private void addStandardVariables(WebContext theCtx, Map theParameterMap) { - addStandardVariable(theCtx, theParameterMap, "encoding"); - addStandardVariable(theCtx, theParameterMap, "pretty"); - } - - private void addStandardVariable(WebContext theCtx, Map theParameterMap, String key) { - if (theParameterMap.containsKey(key) && theParameterMap.get(key).length > 0) { - theCtx.setVariable(key, theParameterMap.get(key)[0]); - } - } - - private Header[] applyHeaderFilters(Header[] theAllHeaders) { - if (myFilterHeaders == null || myFilterHeaders.isEmpty()) { - return theAllHeaders; - } - ArrayList
retVal = new ArrayList
(); - for (Header next : theAllHeaders) { - if (!myFilterHeaders.contains(next.getName().toLowerCase())) { - retVal.add(next); - } - } - return retVal.toArray(new Header[retVal.size()]); - } - - /** - * If set, the headers named here will be stripped from requests/responses before they are displayed to the user. This can be used, for instance, to filter out "Authorization" headers. Note that - * names are not case sensitive. - */ - public void setFilterHeaders(String... theHeaderNames) { - myFilterHeaders = new HashSet(); - if (theHeaderNames != null) { - for (String next : theHeaderNames) { - myFilterHeaders.add(next.toLowerCase()); - } - } - } - - private String parseNarrative(EncodingEnum theCtEnum, String theResultBody) { - try { - IResource resource = theCtEnum.newParser(myCtx).parseResource(theResultBody); - String retVal = resource.getText().getDiv().getValueAsString(); - return StringUtils.defaultString(retVal); - } catch (Exception e) { - ourLog.error("Failed to parse resource", e); - return ""; - } - } - - private final class ProfileResourceResolver implements IResourceResolver { - - @Override - public String getName() { - return getClass().getCanonicalName(); - } - - @Override - public InputStream getResourceAsStream(TemplateProcessingParameters theTemplateProcessingParameters, String theName) { - ourLog.debug("Loading template: {}", theName); - if ("/".equals(theName)) { - return RestfulTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/RestfulTester.html"); - } - if (PUBLIC_TESTER_RESULT_HTML.equals(theName)) { - return RestfulTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTesterResult.html"); - } - - return null; - } - } - -} diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.css b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.css deleted file mode 100644 index 51cffdcc76e..00000000000 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.css +++ /dev/null @@ -1,60 +0,0 @@ -A.selectedFunctionLink { - text-decoration: none; - background: #E0FFE0; - border: 1px #80C080 solid; - color: #005000; -} - -BODY { - font-family: sans-serif; -} - -SPAN.headerName { - color: #505080; -} - -SPAN.headerValue { - color: #70A070; -} - -DIV.bodyHeaderBlock { - background-color: #E0E0E0; - margin-top: 5px; - border-radius: 5px; - padding: 5px; -} - -DIV.textareaWrapper { - position:relative; - height:100%; - width:100%; -} - -DIV.textareaWrapper TEXTAREA { - width:95%; - overflow: scroll; -} - -TABLE.propertyTable { - margin-left: 4px; - width: 95%; -} - -TD.propertyKeyCell { - background-color: #E0E0FF; - border-radius: 3px; - padding: 3px; - width: 100px; -} - -TD.testerNameCell { - background-color: #E0FFE0; - border-radius: 3px; - padding: 3px; -} - -.syntaxhighlighter { - font-size: 0.85em !important; - min-height: 1.4em; - max-width: 800px; -} diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.html b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.html deleted file mode 100644 index f8a4788abfd..00000000000 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.html +++ /dev/null @@ -1,159 +0,0 @@ - - - - - - - - - - - - - - - - - - - -
-
- This is a RESTful server tester, which can be used to send requests to, and receive responses - from the server at the following URL:
- http://foo.com/fhir -
- - - - - - - -
- -
- Software Details -
- - - - - - - - - -
SoftwareHAPI Restful Server
Version1.1.1
- -
- Request Configuration -
- - - - - - - - - -
Encoding - -
Pretty Printing - - -
- -
- - - -
- RESTful Server -
- - - - - -
Supports operations: - conformance - - - - - -
- -
- Resources -
- - - - - - -
Select Resource: - -
- - -
-
- - - - diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.js b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.js deleted file mode 100644 index aafb08c9f1c..00000000000 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/rest/server/tester/PublicTester.js +++ /dev/null @@ -1,360 +0,0 @@ -var uniqueIdSeed = 1; - -function addConfigElementsToForm(theForm) { - var encoding = document.getElementById('configEncoding'); - var pretty = document.getElementById('configPretty'); - - var newEncoding = document.createElement("input"); - newEncoding.type='hidden'; - newEncoding.name='configEncoding'; - newEncoding.value = encoding.value; - theForm.appendChild(newEncoding); - - var newPretty = document.createElement("input"); - newPretty.type='hidden'; - newPretty.name='configPretty'; - if (pretty.checked) { - newPretty.value='on'; - } - newPretty.checked = pretty.checked; - theForm.appendChild(newPretty); -} - -function appendSearchParam(formElement, searchParam) { - var inputId = newUniqueId(); - if (searchParam.type && searchParam.type == 'token') { - formElement.append( - $('', { name: 'param.token.1.' + searchParam.name, placeholder: 'system/namespace', type: 'text', id: inputId }), - $('', { name: 'param.token.2.' + searchParam.name, placeholder: 'value', type: 'text', id: inputId }), - $('