More work on subscriptions
This commit is contained in:
parent
f9e4a3e1b5
commit
c385384269
|
@ -176,13 +176,13 @@ public class ValidatorExamples {
|
|||
IValidationSupport valSupport = new IValidationSupport() {
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(String theSystem) {
|
||||
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
|
||||
// TODO: Implement
|
||||
return false;
|
||||
}
|
||||
|
@ -194,13 +194,13 @@ public class ValidatorExamples {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchCodeSystem(String theSystem) {
|
||||
public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude) {
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -16,12 +16,12 @@ public class LoadingValidationSupport implements IValidationSupport {
|
|||
private static FhirContext myCtx = FhirContext.forDstu2Hl7Org();
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(ConceptSetComponent theInclude) {
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchCodeSystem(String theSystem) {
|
||||
public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -46,12 +46,12 @@ public class LoadingValidationSupport implements IValidationSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(String theSystem) {
|
||||
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(String theCodeSystem, String theCode, String theDisplay) {
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<classpathentry kind="src" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**/*.java" including="**/*.java" kind="src" output="target/test-classes" path="src/test/resources"/>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<classpathentry kind="src" path="src/test/resources"/>
|
||||
<classpathentry kind="src" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**/*.java" including="**/*.java" kind="src" path="src/main/resources"/>
|
||||
<classpathentry including="**/*.java" kind="src" path="target/generated-sources/tinder"/>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="target/generated-resources/tinder">
|
||||
<classpathentry kind="src" path="src/main/resources"/>
|
||||
<classpathentry kind="src" path="target/generated-sources/tinder"/>
|
||||
<classpathentry kind="src" path="target/generated-resources/tinder">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
|
@ -32,5 +32,5 @@
|
|||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2015 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.util.Collections;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2015 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.jpa.dao.IFhirResourceDaoPatient;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.apache.http.client.utils.URLEncodedUtils;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
|
@ -52,6 +53,10 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|||
public class SubscriptionWebsocketHandler extends TextWebSocketHandler implements ISubscriptionWebsocketHandler, Runnable {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionWebsocketHandler.class);
|
||||
|
||||
@Autowired
|
||||
@Qualifier("myFhirContextDstu2")
|
||||
private FhirContext myCtx;
|
||||
|
||||
private ScheduledFuture<?> myScheduleFuture;
|
||||
|
||||
private IState myState = new InitialState();
|
||||
|
@ -88,6 +93,12 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement
|
|||
myState.handleTextMessage(theSession, theMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportError(WebSocketSession theSession, Throwable theException) throws Exception {
|
||||
super.handleTransportError(theSession, theException);
|
||||
ourLog.error("Transport error", theException);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
ourLog.info("Creating scheduled task for subscription websocket connection");
|
||||
|
@ -119,54 +130,26 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement
|
|||
}
|
||||
}
|
||||
|
||||
private class BoundStaticSubscipriptionState implements IState {
|
||||
|
||||
private WebSocketSession mySession;
|
||||
|
||||
public BoundStaticSubscipriptionState(WebSocketSession theSession) {
|
||||
mySession = theSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliver(List<IBaseResource> theResults) {
|
||||
try {
|
||||
String payload = "ping " + mySubscriptionId.getIdPart();
|
||||
ourLog.info("Sending WebSocket message: {}", payload);
|
||||
mySession.sendMessage(new TextMessage(payload));
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
|
||||
try {
|
||||
theSession.sendMessage(new TextMessage("Unexpected client message: " + theMessage.getPayload()));
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closing() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private FhirContext myCtx;
|
||||
|
||||
private class BoundDynamicSubscriptionState implements IState {
|
||||
|
||||
private WebSocketSession mySession;
|
||||
private EncodingEnum myEncoding;
|
||||
private WebSocketSession mySession;
|
||||
|
||||
public BoundDynamicSubscriptionState(WebSocketSession theSession, EncodingEnum theEncoding) {
|
||||
mySession = theSession;
|
||||
myEncoding = theEncoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closing() {
|
||||
ourLog.info("Deleting subscription {}", mySubscriptionId);
|
||||
try {
|
||||
mySubscriptionDao.delete(mySubscriptionId);
|
||||
} catch (Exception e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliver(List<IBaseResource> theResults) {
|
||||
try {
|
||||
|
@ -190,12 +173,37 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class BoundStaticSubscipriptionState implements IState {
|
||||
|
||||
private WebSocketSession mySession;
|
||||
|
||||
public BoundStaticSubscipriptionState(WebSocketSession theSession) {
|
||||
mySession = theSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closing() {
|
||||
ourLog.info("Deleting subscription {}", mySubscriptionId);
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliver(List<IBaseResource> theResults) {
|
||||
try {
|
||||
mySubscriptionDao.delete(mySubscriptionId);
|
||||
} catch (Exception e) {
|
||||
String payload = "ping " + mySubscriptionId.getIdPart();
|
||||
ourLog.info("Sending WebSocket message: {}", payload);
|
||||
mySession.sendMessage(new TextMessage(payload));
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
|
||||
try {
|
||||
theSession.sendMessage(new TextMessage("Unexpected client message: " + theMessage.getPayload()));
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
}
|
||||
|
@ -204,34 +212,37 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement
|
|||
|
||||
private class InitialState implements IState {
|
||||
|
||||
@Override
|
||||
public void deliver(List<IBaseResource> theResults) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
|
||||
String message = theMessage.getPayload();
|
||||
if (message.startsWith("bind ")) {
|
||||
String remaining = message.substring("bind ".length());
|
||||
|
||||
IIdType subscriptionId;
|
||||
if (remaining.contains("?")) {
|
||||
subscriptionId = bingSearch(theSession, remaining);
|
||||
} else {
|
||||
subscriptionId = bindSimple(theSession, remaining);
|
||||
if (subscriptionId == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
private IIdType bindSimple(WebSocketSession theSession, String theBindString) {
|
||||
IdDt id = new IdDt(theBindString);
|
||||
|
||||
if (!id.hasIdPart() || !id.isIdPartValid()) {
|
||||
try {
|
||||
theSession.sendMessage(new TextMessage("bound " + subscriptionId.getIdPart()));
|
||||
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - No ID included"));
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id.hasResourceType() == false) {
|
||||
id = id.withResourceType("Subscription");
|
||||
}
|
||||
|
||||
try {
|
||||
Subscription subscription = mySubscriptionDao.read(id);
|
||||
mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id);
|
||||
mySubscriptionId = subscription.getIdElement();
|
||||
myState = new BoundStaticSubscipriptionState(theSession);
|
||||
} catch (ResourceNotFoundException e) {
|
||||
try {
|
||||
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - Unknown subscription: " + id.getValue()));
|
||||
} catch (IOException e1) {
|
||||
handleFailure(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private IIdType bingSearch(WebSocketSession theSession, String theRemaining) {
|
||||
|
@ -278,52 +289,49 @@ public class SubscriptionWebsocketHandler extends TextWebSocketHandler implement
|
|||
return null;
|
||||
}
|
||||
|
||||
private IIdType bindSimple(WebSocketSession theSession, String theBindString) {
|
||||
IdDt id = new IdDt(theBindString);
|
||||
|
||||
if (!id.hasIdPart() || !id.isIdPartValid()) {
|
||||
try {
|
||||
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - No ID included"));
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id.hasResourceType() == false) {
|
||||
id = id.withResourceType("Subscription");
|
||||
}
|
||||
|
||||
try {
|
||||
Subscription subscription = mySubscriptionDao.read(id);
|
||||
mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id);
|
||||
mySubscriptionId = subscription.getIdElement();
|
||||
myState = new BoundStaticSubscipriptionState(theSession);
|
||||
} catch (ResourceNotFoundException e) {
|
||||
try {
|
||||
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - Unknown subscription: " + id.getValue()));
|
||||
} catch (IOException e1) {
|
||||
handleFailure(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closing() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliver(List<IBaseResource> theResults) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
|
||||
String message = theMessage.getPayload();
|
||||
if (message.startsWith("bind ")) {
|
||||
String remaining = message.substring("bind ".length());
|
||||
|
||||
IIdType subscriptionId;
|
||||
if (remaining.contains("?")) {
|
||||
subscriptionId = bingSearch(theSession, remaining);
|
||||
} else {
|
||||
subscriptionId = bindSimple(theSession, remaining);
|
||||
if (subscriptionId == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
theSession.sendMessage(new TextMessage("bound " + subscriptionId.getIdPart()));
|
||||
} catch (IOException e) {
|
||||
handleFailure(e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private interface IState {
|
||||
|
||||
void deliver(List<IBaseResource> theResults);
|
||||
|
||||
void closing();
|
||||
|
||||
void deliver(List<IBaseResource> theResults);
|
||||
|
||||
void handleTextMessage(WebSocketSession theSession, TextMessage theMessage);
|
||||
|
||||
}
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:websocket="http://www.springframework.org/schema/websocket"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
|
||||
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
|
||||
|
||||
<context:annotation-config />
|
||||
|
@ -26,4 +28,6 @@
|
|||
|
||||
<bean id="mySubscriptionSecurityInterceptor" class="ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptor"/>
|
||||
|
||||
<tx:annotation-driven transaction-manager="myTxManagerDstu2" />
|
||||
|
||||
</beans>
|
|
@ -42,6 +42,13 @@
|
|||
<type>war</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-testpage-overlay</artifactId>
|
||||
<version>1.3-SNAPSHOT</version>
|
||||
<classifier>classes</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.phloc</groupId>
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package ca.uhn.fhirtest.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import ca.uhn.fhir.to.BaseController;
|
||||
import ca.uhn.fhir.to.model.HomeRequest;
|
||||
|
||||
@org.springframework.stereotype.Controller()
|
||||
public class SubscriptionPlaygroundController extends BaseController {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionPlaygroundController.class);
|
||||
|
||||
@RequestMapping(value = { "/subscriptions" })
|
||||
public String subscriptionsHome(final HttpServletRequest theServletRequest, HomeRequest theRequest, final ModelMap theModel) {
|
||||
addCommonParams(theServletRequest, theRequest, theModel);
|
||||
|
||||
theModel.put("notHome", true);
|
||||
theModel.put("extraBreadcrumb", "Subscriptions");
|
||||
|
||||
ourLog.info(logPrefix(theModel) + "Displayed subscriptions playground page");
|
||||
|
||||
return "subscriptions";
|
||||
}
|
||||
|
||||
}
|
|
@ -51,9 +51,9 @@
|
|||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||||
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||||
<property name="entityManagerFactory" ref="entityManagerFactory" />
|
||||
</bean>
|
||||
<tx:annotation-driven transaction-manager="myTxManager" />
|
||||
<tx:annotation-driven transaction-manager="transactionManager" />
|
||||
|
||||
</beans>
|
||||
|
|
|
@ -38,9 +38,9 @@
|
|||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||||
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||||
<property name="entityManagerFactory" ref="entityManagerFactory" />
|
||||
</bean>
|
||||
<tx:annotation-driven transaction-manager="myTxManager" />
|
||||
<tx:annotation-driven transaction-manager="transactionManager" />
|
||||
|
||||
</beans>
|
||||
|
|
|
@ -43,6 +43,13 @@
|
|||
<appender-ref ref="ACCESS" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="debug">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
</logger>
|
||||
|
||||
|
||||
</appender>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security"
|
||||
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
|
||||
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
|
||||
|
||||
<context:component-scan base-package="ca.uhn.fhirtest.mvc" />
|
||||
|
||||
<mvc:annotation-driven />
|
||||
|
||||
</beans>
|
|
@ -34,6 +34,8 @@
|
|||
<property name="socketTimeout" value="10000"/>
|
||||
</bean>
|
||||
|
||||
<context:component-scan base-package="ca.uhn.fhirtest.mvc" />
|
||||
|
||||
<mvc:interceptors>
|
||||
<mvc:interceptor>
|
||||
<mvc:mapping path="/**"/>
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head th:include="tmpl-head :: head">
|
||||
<title>RESTful Tester</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<form action="" method="get" id="outerForm">
|
||||
<input type="hidden" id="serverId" name="serverId"
|
||||
th:value="${serverId}" />
|
||||
|
||||
<div th:replace="tmpl-navbar-top :: top"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<div th:replace="tmpl-navbar-left :: left"></div>
|
||||
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-9 col-md-offset-3 main">
|
||||
|
||||
<div th:replace="tmpl-banner :: banner"></div>
|
||||
|
||||
<!-- ********************************************************** -->
|
||||
<!-- ** Subscriptions Playground ** -->
|
||||
<!-- ********************************************************** -->
|
||||
|
||||
<div class="panel panel-default" th:if="${resourceName.empty}">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Subscriptions Playground</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="container-fluid">
|
||||
<p>
|
||||
This page is a test playground for WebSocket Subscriptions
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nav tabs -->
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li role="presentation" class="active"><a href="#wss"
|
||||
aria-controls="wss" role="tab" data-toggle="tab">Static
|
||||
Websocket</a></li>
|
||||
<li role="presentation"><a href="#wsd" aria-controls="wsd"
|
||||
role="tab" data-toggle="tab">Dynamic Websocket</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="wss">...1</div>
|
||||
|
||||
<!-- Dynamic WebSocket -->
|
||||
<div role="tabpanel" class="tab-pane" id="wsd">
|
||||
<script type="text/javascript">
|
||||
function dwsCreate() {
|
||||
try {
|
||||
var socket = new WebSocket("ws://localhost:8888/websocket/dstu2");
|
||||
alert('Socket Status: '+socket.readyState);
|
||||
|
||||
socket.onopen = function() {
|
||||
alert("Opening Socket: " + socket.readyState);
|
||||
socket.send("bind Observation?");
|
||||
}
|
||||
socket.onmessage = function(msg) {
|
||||
alert("New message: " + msg);
|
||||
}
|
||||
socket.onerror = function(error) {
|
||||
alert("error: " + error);
|
||||
}
|
||||
} catch (exception) {
|
||||
alert('Error'+exception);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
Enter a criteria in the text box below and then click subscribe to
|
||||
create a dynamic subscription and then display the results as they
|
||||
arrive.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="boorder-top: 10px;">
|
||||
<div class="col-sm-7">
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">
|
||||
Criteria
|
||||
</div>
|
||||
<input class="form-control" placeholder="e.g: Observation?patient=123" id="dws_criteria"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<button type="button" class="btn btn-default btn-primary btn-block" id="dws_create" onclick="dwsCreate();"><i class="glyphicon glyphicon-cog"></i> Subscribe</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Dynamic Subscription Results -->
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Subscription Results</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
This will be status...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- List group -->
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">Cras justo odio</li>
|
||||
<li class="list-group-item">Dapibus ac facilisis in</li>
|
||||
<li class="list-group-item">Morbi leo risus</li>
|
||||
<li class="list-group-item">Porta ac consectetur ac</li>
|
||||
<li class="list-group-item">Vestibulum at eros</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<div th:replace="tmpl-footer :: footer"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -67,7 +67,7 @@ public class UhnFhirTestApp {
|
|||
client.create(p1);
|
||||
|
||||
List<IResource> resources = ctx.newJsonParser().parseBundle(IOUtils.toString(UhnFhirTestApp.class.getResourceAsStream("/bundle.json"))).toListOfResources();
|
||||
client.transaction().withResources(resources).execute();
|
||||
// client.transaction().withResources(resources).execute();
|
||||
|
||||
// for (int i = 0; i < 1000; i++) {
|
||||
//
|
||||
|
|
|
@ -44,11 +44,7 @@
|
|||
<artifactId>hapi-fhir-structures-dstu2</artifactId>
|
||||
<version>1.3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dev</artifactId>
|
||||
<version>0.9</version>
|
||||
</dependency>-->
|
||||
<!--<dependency> <groupId>ca.uhn.hapi.fhir</groupId> <artifactId>hapi-fhir-structures-dev</artifactId> <version>0.9</version> </dependency> -->
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf</artifactId>
|
||||
|
@ -202,6 +198,13 @@
|
|||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<configuration>
|
||||
<attachClasses>true</attachClasses>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
|
|
@ -0,0 +1,637 @@
|
|||
package ca.uhn.fhir.to;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
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.apache.http.entity.HttpEntityWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
|
||||
import ca.uhn.fhir.model.primitive.DecimalDt;
|
||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.client.GenericClient;
|
||||
import ca.uhn.fhir.rest.client.IClientInterceptor;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.to.model.HomeRequest;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
|
||||
public class BaseController {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseController.class);
|
||||
static final String PARAM_RESOURCE = "resource";
|
||||
static final String RESOURCE_COUNT_EXT_URL = "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount";
|
||||
|
||||
@Autowired
|
||||
protected TesterConfig myConfig;
|
||||
private Map<FhirVersionEnum, FhirContext> myContexts = new HashMap<FhirVersionEnum, FhirContext>();
|
||||
private List<String> myFilterHeaders;
|
||||
@Autowired
|
||||
private TemplateEngine myTemplateEngine;
|
||||
|
||||
public BaseController() {
|
||||
super();
|
||||
}
|
||||
|
||||
protected IResource addCommonParams(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
if (myConfig.getDebugTemplatesMode()) {
|
||||
myTemplateEngine.getCacheManager().clearAllCaches();
|
||||
}
|
||||
|
||||
final String serverId = theRequest.getServerIdWithDefault(myConfig);
|
||||
final String serverBase = theRequest.getServerBase(theServletRequest, myConfig);
|
||||
final String serverName = theRequest.getServerName(myConfig);
|
||||
theModel.put("serverId", serverId);
|
||||
theModel.put("base", serverBase);
|
||||
theModel.put("baseName", serverName);
|
||||
theModel.put("resourceName", defaultString(theRequest.getResource()));
|
||||
theModel.put("encoding", theRequest.getEncoding());
|
||||
theModel.put("pretty", theRequest.getPretty());
|
||||
theModel.put("_summary", theRequest.get_summary());
|
||||
theModel.put("serverEntries", myConfig.getIdToServerName());
|
||||
|
||||
return loadAndAddConf(theServletRequest, theRequest, theModel);
|
||||
}
|
||||
|
||||
private Header[] applyHeaderFilters(Header[] theAllHeaders) {
|
||||
if (myFilterHeaders == null || myFilterHeaders.isEmpty()) {
|
||||
return theAllHeaders;
|
||||
}
|
||||
ArrayList<Header> retVal = new ArrayList<Header>();
|
||||
for (Header next : theAllHeaders) {
|
||||
if (!myFilterHeaders.contains(next.getName().toLowerCase())) {
|
||||
retVal.add(next);
|
||||
}
|
||||
}
|
||||
return retVal.toArray(new Header[retVal.size()]);
|
||||
}
|
||||
|
||||
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;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
} else if (nextChar == '\\' && nextChar2 == '"') {
|
||||
b.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else {
|
||||
if (nextChar == ':') {
|
||||
inValue = true;
|
||||
b.append(nextChar);
|
||||
} else if (nextChar == '[' || nextChar == '{') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '}' || nextChar == '}' || nextChar == ',') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
if (inValue) {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
} else {
|
||||
b.append("<span class='hlTagName'>"");
|
||||
}
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else if (nextChar == ':') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
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;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else if (inTag) {
|
||||
if (nextChar == '&' && nextChar2 == 'g' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("</span><span class='hlControl'>></span>");
|
||||
inTag = false;
|
||||
i += 3;
|
||||
} else if (nextChar == ' ') {
|
||||
b.append("</span><span class='hlAttr'>");
|
||||
b.append(nextChar);
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
} else {
|
||||
if (nextChar == '&' && nextChar2 == 'l' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("<span class='hlControl'><</span><span class='hlTagName'>");
|
||||
inTag = true;
|
||||
i += 3;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private String formatUrl(String theUrlBase, String theResultBody) {
|
||||
String str = theResultBody;
|
||||
if (str == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
try {
|
||||
str = URLDecoder.decode(str, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
ourLog.error("Should not happen", e);
|
||||
}
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("<span class='hlUrlBase'>");
|
||||
|
||||
boolean inParams = false;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char nextChar = str.charAt(i);
|
||||
// char nextChar2 = i < str.length()-2 ? str.charAt(i+1):' ';
|
||||
// char nextChar3 = i < str.length()-2 ? str.charAt(i+2):' ';
|
||||
if (!inParams) {
|
||||
if (nextChar == '?') {
|
||||
inParams = true;
|
||||
b.append("</span><wbr /><span class='hlControl'>?</span><span class='hlTagName'>");
|
||||
} else {
|
||||
if (i == theUrlBase.length()) {
|
||||
b.append("</span><wbr /><span class='hlText'>");
|
||||
}
|
||||
b.append(nextChar);
|
||||
}
|
||||
} else {
|
||||
if (nextChar == '&') {
|
||||
b.append("</span><wbr /><span class='hlControl'>&</span><span class='hlTagName'>");
|
||||
} else if (nextChar == '=') {
|
||||
b.append("</span><span class='hlControl'>=</span><span class='hlAttr'>");
|
||||
// }else if (nextChar=='%' && Character.isLetterOrDigit(nextChar2)&& Character.isLetterOrDigit(nextChar3)) {
|
||||
// URLDecoder.decode(s, enc)
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams) {
|
||||
b.append("</span>");
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
protected FhirContext getContext(HomeRequest theRequest) {
|
||||
FhirVersionEnum version = theRequest.getFhirVersion(myConfig);
|
||||
FhirContext retVal = myContexts.get(version);
|
||||
if (retVal == null) {
|
||||
retVal = new FhirContext(version);
|
||||
myContexts.put(version, retVal);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected RuntimeResourceDefinition getResourceType(HomeRequest theRequest, HttpServletRequest theReq) throws ServletException {
|
||||
String resourceName = StringUtils.defaultString(theReq.getParameter(PARAM_RESOURCE));
|
||||
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(resourceName);
|
||||
if (def == null) {
|
||||
throw new ServletException("Invalid resourceName: " + resourceName);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
protected ResultType handleClientException(GenericClient theClient, Exception e, ModelMap theModel) {
|
||||
ResultType returnsResource;
|
||||
returnsResource = ResultType.NONE;
|
||||
ourLog.warn("Failed to invoke server", e);
|
||||
|
||||
if (theClient.getLastResponse() == null) {
|
||||
theModel.put("errorMsg", "Error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return returnsResource;
|
||||
}
|
||||
|
||||
private IResource loadAndAddConf(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
switch (theRequest.getFhirVersion(myConfig)) {
|
||||
case DEV:
|
||||
return loadAndAddConfDstu2(theServletRequest, theRequest, theModel);
|
||||
case DSTU1:
|
||||
return loadAndAddConfDstu1(theServletRequest, theRequest, theModel);
|
||||
case DSTU2:
|
||||
return loadAndAddConfDstu2(theServletRequest, theRequest, theModel);
|
||||
}
|
||||
throw new IllegalStateException("Unknown version: " + theRequest.getFhirVersion(myConfig));
|
||||
}
|
||||
|
||||
private Conformance loadAndAddConfDstu1(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
CaptureInterceptor interceptor = new CaptureInterceptor();
|
||||
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
|
||||
|
||||
Conformance conformance;
|
||||
try {
|
||||
conformance = (Conformance) client.conformance();
|
||||
} catch (Exception e) {
|
||||
ourLog.warn("Failed to load conformance statement", e);
|
||||
theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
|
||||
conformance = new Conformance();
|
||||
}
|
||||
|
||||
theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance));
|
||||
|
||||
Map<String, Number> resourceCounts = new HashMap<String, Number>();
|
||||
long total = 0;
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
for (ca.uhn.fhir.model.dstu.resource.Conformance.RestResource nextResource : nextRest.getResource()) {
|
||||
List<ExtensionDt> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
theModel.put("resourceCounts", resourceCounts);
|
||||
|
||||
if (total > 0) {
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
Collections.sort(nextRest.getResource(), new Comparator<ca.uhn.fhir.model.dstu.resource.Conformance.RestResource>() {
|
||||
@Override
|
||||
public int compare(ca.uhn.fhir.model.dstu.resource.Conformance.RestResource theO1, ca.uhn.fhir.model.dstu.resource.Conformance.RestResource theO2) {
|
||||
DecimalDt count1 = new DecimalDt();
|
||||
List<ExtensionDt> count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count1exts != null && count1exts.size() > 0) {
|
||||
count1 = (DecimalDt) count1exts.get(0).getValue();
|
||||
}
|
||||
DecimalDt count2 = new DecimalDt();
|
||||
List<ExtensionDt> 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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
theModel.put("conf", conformance);
|
||||
theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED);
|
||||
|
||||
return conformance;
|
||||
}
|
||||
|
||||
private IResource loadAndAddConfDstu2(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
CaptureInterceptor interceptor = new CaptureInterceptor();
|
||||
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
|
||||
|
||||
ca.uhn.fhir.model.dstu2.resource.Conformance conformance;
|
||||
try {
|
||||
conformance = (ca.uhn.fhir.model.dstu2.resource.Conformance) client.conformance();
|
||||
} catch (Exception e) {
|
||||
ourLog.warn("Failed to load conformance statement", e);
|
||||
theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
|
||||
conformance = new ca.uhn.fhir.model.dstu2.resource.Conformance();
|
||||
}
|
||||
|
||||
theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance));
|
||||
|
||||
Map<String, Number> resourceCounts = new HashMap<String, Number>();
|
||||
long total = 0;
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource nextResource : nextRest.getResource()) {
|
||||
List<ExtensionDt> exts = nextResource.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (exts != null && exts.size() > 0) {
|
||||
Number nextCount = ((DecimalDt) (exts.get(0).getValue())).getValueAsNumber();
|
||||
resourceCounts.put(nextResource.getTypeElement().getValue(), nextCount);
|
||||
total += nextCount.longValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
theModel.put("resourceCounts", resourceCounts);
|
||||
|
||||
if (total > 0) {
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
|
||||
Collections.sort(nextRest.getResource(), new Comparator<ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource>() {
|
||||
@Override
|
||||
public int compare(ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO1, ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO2) {
|
||||
DecimalDt count1 = new DecimalDt();
|
||||
List<ExtensionDt> count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count1exts != null && count1exts.size() > 0) {
|
||||
count1 = (DecimalDt) count1exts.get(0).getValue();
|
||||
}
|
||||
DecimalDt count2 = new DecimalDt();
|
||||
List<ExtensionDt> 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.getTypeElement().getValue().compareTo(theO2.getTypeElement().getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
theModel.put("conf", conformance);
|
||||
theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED);
|
||||
|
||||
return conformance;
|
||||
}
|
||||
|
||||
protected String logPrefix(ModelMap theModel) {
|
||||
return "[server=" + theModel.get("serverId") + "] - ";
|
||||
}
|
||||
|
||||
private String parseNarrative(HomeRequest theRequest, EncodingEnum theCtEnum, String theResultBody) {
|
||||
try {
|
||||
IResource resource = (IResource) theCtEnum.newParser(getContext(theRequest)).parseResource(theResultBody);
|
||||
String retVal = resource.getText().getDiv().getValueAsString();
|
||||
return StringUtils.defaultString(retVal);
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to parse resource", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
protected String preProcessMessageBody(String theBody) {
|
||||
if (theBody == null) {
|
||||
return "";
|
||||
}
|
||||
String retVal = theBody.trim();
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (int i = 0; i < retVal.length(); i++) {
|
||||
char nextChar = retVal.charAt(i);
|
||||
int nextCharI = nextChar;
|
||||
if (nextCharI == 65533) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
if (nextCharI == 160) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
if (nextCharI == 194) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
b.append(nextChar);
|
||||
}
|
||||
retVal = b.toString();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected void processAndAddLastClientInvocation(GenericClient theClient, ResultType theResultType, ModelMap theModelMap, long theLatency, String outcomeDescription, CaptureInterceptor theInterceptor, HomeRequest theRequest) {
|
||||
try {
|
||||
HttpRequestBase lastRequest = theInterceptor.getLastRequest();
|
||||
HttpResponse lastResponse = theInterceptor.getLastResponse();
|
||||
String requestBody = null;
|
||||
String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null;
|
||||
String action = lastRequest != null ? lastRequest.getMethod() : null;
|
||||
String resultStatus = lastResponse != null ? lastResponse.getStatusLine().toString() : null;
|
||||
String resultBody = StringUtils.defaultString(theInterceptor.getLastResponseBody());
|
||||
|
||||
if (lastRequest instanceof HttpEntityEnclosingRequest) {
|
||||
HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity();
|
||||
if (entity.isRepeatable()) {
|
||||
requestBody = IOUtils.toString(entity.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
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 (theResultType == ResultType.RESOURCE) {
|
||||
narrativeString = parseNarrative(theRequest, ctEnum, resultBody);
|
||||
resultDescription.append("JSON resource");
|
||||
} else if (theResultType == ResultType.BUNDLE) {
|
||||
resultDescription.append("JSON bundle");
|
||||
bundle = getContext(theRequest).newJsonParser().parseBundle(resultBody);
|
||||
}
|
||||
break;
|
||||
case XML:
|
||||
default:
|
||||
if (theResultType == ResultType.RESOURCE) {
|
||||
narrativeString = parseNarrative(theRequest, ctEnum, resultBody);
|
||||
resultDescription.append("XML resource");
|
||||
} else if (theResultType == ResultType.BUNDLE) {
|
||||
resultDescription.append("XML bundle");
|
||||
bundle = getContext(theRequest).newXmlParser().parseBundle(resultBody);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DSTU2 no longer has a title in the bundle format, but it's still useful here..
|
||||
*/
|
||||
if (bundle != null) {
|
||||
INarrativeGenerator gen = getContext(theRequest).getNarrativeGenerator();
|
||||
if (gen != null) {
|
||||
for (BundleEntry next : bundle.getEntries()) {
|
||||
if (next.getTitle().isEmpty() && next.getResource() != null) {
|
||||
String title = gen.generateTitle(next.getResource());
|
||||
next.getTitle().setValue(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
theModelMap.put("outcomeDescription", outcomeDescription);
|
||||
theModelMap.put("resultDescription", resultDescription.toString());
|
||||
theModelMap.put("action", action);
|
||||
theModelMap.put("bundle", bundle);
|
||||
theModelMap.put("resultStatus", resultStatus);
|
||||
|
||||
theModelMap.put("requestUrl", requestUrl);
|
||||
theModelMap.put("requestUrlText", formatUrl(theClient.getUrlBase(), requestUrl));
|
||||
|
||||
String requestBodyText = format(requestBody, ctEnum);
|
||||
theModelMap.put("requestBody", requestBodyText);
|
||||
|
||||
String resultBodyText = format(resultBody, ctEnum);
|
||||
theModelMap.put("resultBody", resultBodyText);
|
||||
|
||||
theModelMap.put("resultBodyIsLong", resultBodyText.length() > 1000);
|
||||
theModelMap.put("requestHeaders", requestHeaders);
|
||||
theModelMap.put("responseHeaders", responseHeaders);
|
||||
theModelMap.put("narrative", narrativeString);
|
||||
theModelMap.put("latencyMs", theLatency);
|
||||
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during processing", e);
|
||||
theModelMap.put("errorMsg", "Error during processing: " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class CaptureInterceptor implements IClientInterceptor {
|
||||
|
||||
private HttpRequestBase myLastRequest;
|
||||
private HttpResponse myLastResponse;
|
||||
private String myResponseBody;
|
||||
|
||||
public HttpRequestBase getLastRequest() {
|
||||
return myLastRequest;
|
||||
}
|
||||
|
||||
public HttpResponse getLastResponse() {
|
||||
return myLastResponse;
|
||||
}
|
||||
|
||||
public String getLastResponseBody() {
|
||||
return myResponseBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptRequest(HttpRequestBase theRequest) {
|
||||
assert myLastRequest == null;
|
||||
myLastRequest = theRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptResponse(HttpResponse theResponse) throws IOException {
|
||||
assert myLastResponse == null;
|
||||
myLastResponse = theResponse;
|
||||
|
||||
HttpEntity respEntity = theResponse.getEntity();
|
||||
if (respEntity != null) {
|
||||
final byte[] bytes;
|
||||
try {
|
||||
bytes = IOUtils.toByteArray(respEntity.getContent());
|
||||
} catch (IllegalStateException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
myResponseBody = new String(bytes, "UTF-8");
|
||||
theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyEntityWrapper extends HttpEntityWrapper {
|
||||
|
||||
private byte[] myBytes;
|
||||
|
||||
public MyEntityWrapper(HttpEntity theWrappedEntity, byte[] theBytes) {
|
||||
super(theWrappedEntity);
|
||||
myBytes = theBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getContent() throws IOException {
|
||||
return new ByteArrayInputStream(myBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream theOutstream) throws IOException {
|
||||
theOutstream.write(myBytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected enum ResultType {
|
||||
BUNDLE, NONE, RESOURCE, TAGLIST
|
||||
}
|
||||
|
||||
}
|
|
@ -2,19 +2,10 @@ package ca.uhn.fhir.to;
|
|||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.json.Json;
|
||||
|
@ -22,27 +13,13 @@ import javax.json.stream.JsonGenerator;
|
|||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
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.apache.http.entity.HttpEntityWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
|
@ -55,13 +32,10 @@ import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
|
|||
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||
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.narrative.INarrativeGenerator;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.client.GenericClient;
|
||||
import ca.uhn.fhir.rest.client.IClientInterceptor;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.gclient.ICreateTyped;
|
||||
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||
|
@ -73,27 +47,14 @@ import ca.uhn.fhir.rest.gclient.StringClientParam;
|
|||
import ca.uhn.fhir.rest.gclient.TokenClientParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.to.model.HomeRequest;
|
||||
import ca.uhn.fhir.to.model.ResourceRequest;
|
||||
import ca.uhn.fhir.to.model.TransactionRequest;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
|
||||
@org.springframework.stereotype.Controller()
|
||||
public class Controller {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Controller.class);
|
||||
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";
|
||||
|
||||
@Autowired
|
||||
private TesterConfig myConfig;
|
||||
|
||||
private Map<FhirVersionEnum, FhirContext> myContexts = new HashMap<FhirVersionEnum, FhirContext>();
|
||||
|
||||
private List<String> myFilterHeaders;
|
||||
|
||||
@Autowired
|
||||
private TemplateEngine myTemplateEngine;
|
||||
public class Controller extends BaseController {
|
||||
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Controller.class);
|
||||
|
||||
@RequestMapping(value = { "/about" })
|
||||
public String actionAbout(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
|
@ -558,39 +519,6 @@ public class Controller {
|
|||
return "result";
|
||||
}
|
||||
|
||||
private IResource addCommonParams(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
if (myConfig.getDebugTemplatesMode()) {
|
||||
myTemplateEngine.getCacheManager().clearAllCaches();
|
||||
}
|
||||
|
||||
final String serverId = theRequest.getServerIdWithDefault(myConfig);
|
||||
final String serverBase = theRequest.getServerBase(theServletRequest, myConfig);
|
||||
final String serverName = theRequest.getServerName(myConfig);
|
||||
theModel.put("serverId", serverId);
|
||||
theModel.put("base", serverBase);
|
||||
theModel.put("baseName", serverName);
|
||||
theModel.put("resourceName", defaultString(theRequest.getResource()));
|
||||
theModel.put("encoding", theRequest.getEncoding());
|
||||
theModel.put("pretty", theRequest.getPretty());
|
||||
theModel.put("_summary", theRequest.get_summary());
|
||||
theModel.put("serverEntries", myConfig.getIdToServerName());
|
||||
|
||||
return loadAndAddConf(theServletRequest, theRequest, theModel);
|
||||
}
|
||||
|
||||
private Header[] applyHeaderFilters(Header[] theAllHeaders) {
|
||||
if (myFilterHeaders == null || myFilterHeaders.isEmpty()) {
|
||||
return theAllHeaders;
|
||||
}
|
||||
ArrayList<Header> retVal = new ArrayList<Header>();
|
||||
for (Header next : theAllHeaders) {
|
||||
if (!myFilterHeaders.contains(next.getName().toLowerCase())) {
|
||||
retVal.add(next);
|
||||
}
|
||||
}
|
||||
return retVal.toArray(new Header[retVal.size()]);
|
||||
}
|
||||
|
||||
private void doActionCreateOrValidate(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel, String theMethod) {
|
||||
boolean validate = "validate".equals(theMethod);
|
||||
|
||||
|
@ -805,197 +733,6 @@ public class Controller {
|
|||
return haveSearchParams;
|
||||
}
|
||||
|
||||
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;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
} else if (nextChar == '\\' && nextChar2 == '"') {
|
||||
b.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else {
|
||||
if (nextChar == ':') {
|
||||
inValue = true;
|
||||
b.append(nextChar);
|
||||
} else if (nextChar == '[' || nextChar == '{') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '}' || nextChar == '}' || nextChar == ',') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
if (inValue) {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
} else {
|
||||
b.append("<span class='hlTagName'>"");
|
||||
}
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else if (nextChar == ':') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
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;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else if (inTag) {
|
||||
if (nextChar == '&' && nextChar2 == 'g' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("</span><span class='hlControl'>></span>");
|
||||
inTag = false;
|
||||
i += 3;
|
||||
} else if (nextChar == ' ') {
|
||||
b.append("</span><span class='hlAttr'>");
|
||||
b.append(nextChar);
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
} else {
|
||||
if (nextChar == '&' && nextChar2 == 'l' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("<span class='hlControl'><</span><span class='hlTagName'>");
|
||||
inTag = true;
|
||||
i += 3;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private String formatUrl(String theUrlBase, String theResultBody) {
|
||||
String str = theResultBody;
|
||||
if (str == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
try {
|
||||
str = URLDecoder.decode(str, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
ourLog.error("Should not happen", e);
|
||||
}
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("<span class='hlUrlBase'>");
|
||||
|
||||
boolean inParams = false;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char nextChar = str.charAt(i);
|
||||
// char nextChar2 = i < str.length()-2 ? str.charAt(i+1):' ';
|
||||
// char nextChar3 = i < str.length()-2 ? str.charAt(i+2):' ';
|
||||
if (!inParams) {
|
||||
if (nextChar == '?') {
|
||||
inParams = true;
|
||||
b.append("</span><wbr /><span class='hlControl'>?</span><span class='hlTagName'>");
|
||||
} else {
|
||||
if (i == theUrlBase.length()) {
|
||||
b.append("</span><wbr /><span class='hlText'>");
|
||||
}
|
||||
b.append(nextChar);
|
||||
}
|
||||
} else {
|
||||
if (nextChar == '&') {
|
||||
b.append("</span><wbr /><span class='hlControl'>&</span><span class='hlTagName'>");
|
||||
} else if (nextChar == '=') {
|
||||
b.append("</span><span class='hlControl'>=</span><span class='hlAttr'>");
|
||||
// }else if (nextChar=='%' && Character.isLetterOrDigit(nextChar2)&& Character.isLetterOrDigit(nextChar3)) {
|
||||
// URLDecoder.decode(s, enc)
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inParams) {
|
||||
b.append("</span>");
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private FhirContext getContext(HomeRequest theRequest) {
|
||||
FhirVersionEnum version = theRequest.getFhirVersion(myConfig);
|
||||
FhirContext retVal = myContexts.get(version);
|
||||
if (retVal == null) {
|
||||
retVal = new FhirContext(version);
|
||||
myContexts.put(version, retVal);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private RuntimeResourceDefinition getResourceType(HomeRequest theRequest, HttpServletRequest theReq) throws ServletException {
|
||||
String resourceName = StringUtils.defaultString(theReq.getParameter(PARAM_RESOURCE));
|
||||
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(resourceName);
|
||||
if (def == null) {
|
||||
throw new ServletException("Invalid resourceName: " + resourceName);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
private ResultType handleClientException(GenericClient theClient, Exception e, ModelMap theModel) {
|
||||
ResultType returnsResource;
|
||||
returnsResource = ResultType.NONE;
|
||||
ourLog.warn("Failed to invoke server", e);
|
||||
|
||||
if (theClient.getLastResponse() == null) {
|
||||
theModel.put("errorMsg", "Error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return returnsResource;
|
||||
}
|
||||
|
||||
private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery theQuery, JsonGenerator theClientCodeJsonWriter) {
|
||||
String nextName = theReq.getParameter("param." + paramIdxString + ".name");
|
||||
if (isBlank(nextName)) {
|
||||
|
@ -1099,349 +836,4 @@ public class Controller {
|
|||
return true;
|
||||
}
|
||||
|
||||
private IResource loadAndAddConf(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
switch (theRequest.getFhirVersion(myConfig)) {
|
||||
case DEV:
|
||||
return loadAndAddConfDstu2(theServletRequest, theRequest, theModel);
|
||||
case DSTU1:
|
||||
return loadAndAddConfDstu1(theServletRequest, theRequest, theModel);
|
||||
case DSTU2:
|
||||
return loadAndAddConfDstu2(theServletRequest, theRequest, theModel);
|
||||
}
|
||||
throw new IllegalStateException("Unknown version: " + theRequest.getFhirVersion(myConfig));
|
||||
}
|
||||
|
||||
private Conformance loadAndAddConfDstu1(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
CaptureInterceptor interceptor = new CaptureInterceptor();
|
||||
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
|
||||
|
||||
Conformance conformance;
|
||||
try {
|
||||
conformance = (Conformance) client.conformance();
|
||||
} catch (Exception e) {
|
||||
ourLog.warn("Failed to load conformance statement", e);
|
||||
theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
|
||||
conformance = new Conformance();
|
||||
}
|
||||
|
||||
theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance));
|
||||
|
||||
Map<String, Number> resourceCounts = new HashMap<String, Number>();
|
||||
long total = 0;
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
for (RestResource nextResource : nextRest.getResource()) {
|
||||
List<ExtensionDt> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
theModel.put("resourceCounts", resourceCounts);
|
||||
|
||||
if (total > 0) {
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
Collections.sort(nextRest.getResource(), new Comparator<RestResource>() {
|
||||
@Override
|
||||
public int compare(RestResource theO1, RestResource theO2) {
|
||||
DecimalDt count1 = new DecimalDt();
|
||||
List<ExtensionDt> count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count1exts != null && count1exts.size() > 0) {
|
||||
count1 = (DecimalDt) count1exts.get(0).getValue();
|
||||
}
|
||||
DecimalDt count2 = new DecimalDt();
|
||||
List<ExtensionDt> 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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
theModel.put("conf", conformance);
|
||||
theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED);
|
||||
|
||||
return conformance;
|
||||
}
|
||||
|
||||
private IResource loadAndAddConfDstu2(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
|
||||
CaptureInterceptor interceptor = new CaptureInterceptor();
|
||||
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
|
||||
|
||||
ca.uhn.fhir.model.dstu2.resource.Conformance conformance;
|
||||
try {
|
||||
conformance = (ca.uhn.fhir.model.dstu2.resource.Conformance) client.conformance();
|
||||
} catch (Exception e) {
|
||||
ourLog.warn("Failed to load conformance statement", e);
|
||||
theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
|
||||
conformance = new ca.uhn.fhir.model.dstu2.resource.Conformance();
|
||||
}
|
||||
|
||||
theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance));
|
||||
|
||||
Map<String, Number> resourceCounts = new HashMap<String, Number>();
|
||||
long total = 0;
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource nextResource : nextRest.getResource()) {
|
||||
List<ExtensionDt> exts = nextResource.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (exts != null && exts.size() > 0) {
|
||||
Number nextCount = ((DecimalDt) (exts.get(0).getValue())).getValueAsNumber();
|
||||
resourceCounts.put(nextResource.getTypeElement().getValue(), nextCount);
|
||||
total += nextCount.longValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
theModel.put("resourceCounts", resourceCounts);
|
||||
|
||||
if (total > 0) {
|
||||
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
|
||||
Collections.sort(nextRest.getResource(), new Comparator<ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource>() {
|
||||
@Override
|
||||
public int compare(ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO1, ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO2) {
|
||||
DecimalDt count1 = new DecimalDt();
|
||||
List<ExtensionDt> count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count1exts != null && count1exts.size() > 0) {
|
||||
count1 = (DecimalDt) count1exts.get(0).getValue();
|
||||
}
|
||||
DecimalDt count2 = new DecimalDt();
|
||||
List<ExtensionDt> 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.getTypeElement().getValue().compareTo(theO2.getTypeElement().getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
theModel.put("conf", conformance);
|
||||
theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED);
|
||||
|
||||
return conformance;
|
||||
}
|
||||
|
||||
private String logPrefix(ModelMap theModel) {
|
||||
return "[server=" + theModel.get("serverId") + "] - ";
|
||||
}
|
||||
|
||||
private String parseNarrative(HomeRequest theRequest, EncodingEnum theCtEnum, String theResultBody) {
|
||||
try {
|
||||
IResource resource = (IResource) theCtEnum.newParser(getContext(theRequest)).parseResource(theResultBody);
|
||||
String retVal = resource.getText().getDiv().getValueAsString();
|
||||
return StringUtils.defaultString(retVal);
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to parse resource", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private String preProcessMessageBody(String theBody) {
|
||||
if (theBody == null) {
|
||||
return "";
|
||||
}
|
||||
String retVal = theBody.trim();
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (int i = 0; i < retVal.length(); i++) {
|
||||
char nextChar = retVal.charAt(i);
|
||||
int nextCharI = nextChar;
|
||||
if (nextCharI == 65533) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
if (nextCharI == 160) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
if (nextCharI == 194) {
|
||||
b.append(' ');
|
||||
continue;
|
||||
}
|
||||
b.append(nextChar);
|
||||
}
|
||||
retVal = b.toString();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void processAndAddLastClientInvocation(GenericClient theClient, ResultType theResultType, ModelMap theModelMap, long theLatency, String outcomeDescription,
|
||||
CaptureInterceptor theInterceptor, HomeRequest theRequest) {
|
||||
try {
|
||||
HttpRequestBase lastRequest = theInterceptor.getLastRequest();
|
||||
HttpResponse lastResponse = theInterceptor.getLastResponse();
|
||||
String requestBody = null;
|
||||
String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null;
|
||||
String action = lastRequest != null ? lastRequest.getMethod() : null;
|
||||
String resultStatus = lastResponse != null ? lastResponse.getStatusLine().toString() : null;
|
||||
String resultBody = StringUtils.defaultString(theInterceptor.getLastResponseBody());
|
||||
|
||||
if (lastRequest instanceof HttpEntityEnclosingRequest) {
|
||||
HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity();
|
||||
if (entity.isRepeatable()) {
|
||||
requestBody = IOUtils.toString(entity.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
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 (theResultType == ResultType.RESOURCE) {
|
||||
narrativeString = parseNarrative(theRequest, ctEnum, resultBody);
|
||||
resultDescription.append("JSON resource");
|
||||
} else if (theResultType == ResultType.BUNDLE) {
|
||||
resultDescription.append("JSON bundle");
|
||||
bundle = getContext(theRequest).newJsonParser().parseBundle(resultBody);
|
||||
}
|
||||
break;
|
||||
case XML:
|
||||
default:
|
||||
if (theResultType == ResultType.RESOURCE) {
|
||||
narrativeString = parseNarrative(theRequest, ctEnum, resultBody);
|
||||
resultDescription.append("XML resource");
|
||||
} else if (theResultType == ResultType.BUNDLE) {
|
||||
resultDescription.append("XML bundle");
|
||||
bundle = getContext(theRequest).newXmlParser().parseBundle(resultBody);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DSTU2 no longer has a title in the bundle format, but it's still useful here..
|
||||
*/
|
||||
if (bundle != null) {
|
||||
INarrativeGenerator gen = getContext(theRequest).getNarrativeGenerator();
|
||||
if (gen != null) {
|
||||
for (BundleEntry next : bundle.getEntries()) {
|
||||
if (next.getTitle().isEmpty() && next.getResource() != null) {
|
||||
String title = gen.generateTitle(next.getResource());
|
||||
next.getTitle().setValue(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
theModelMap.put("outcomeDescription", outcomeDescription);
|
||||
theModelMap.put("resultDescription", resultDescription.toString());
|
||||
theModelMap.put("action", action);
|
||||
theModelMap.put("bundle", bundle);
|
||||
theModelMap.put("resultStatus", resultStatus);
|
||||
|
||||
theModelMap.put("requestUrl", requestUrl);
|
||||
theModelMap.put("requestUrlText", formatUrl(theClient.getUrlBase(), requestUrl));
|
||||
|
||||
String requestBodyText = format(requestBody, ctEnum);
|
||||
theModelMap.put("requestBody", requestBodyText);
|
||||
|
||||
String resultBodyText = format(resultBody, ctEnum);
|
||||
theModelMap.put("resultBody", resultBodyText);
|
||||
|
||||
theModelMap.put("resultBodyIsLong", resultBodyText.length() > 1000);
|
||||
theModelMap.put("requestHeaders", requestHeaders);
|
||||
theModelMap.put("responseHeaders", responseHeaders);
|
||||
theModelMap.put("narrative", narrativeString);
|
||||
theModelMap.put("latencyMs", theLatency);
|
||||
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during processing", e);
|
||||
theModelMap.put("errorMsg", "Error during processing: " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class CaptureInterceptor implements IClientInterceptor {
|
||||
|
||||
private HttpRequestBase myLastRequest;
|
||||
private HttpResponse myLastResponse;
|
||||
private String myResponseBody;
|
||||
|
||||
public HttpRequestBase getLastRequest() {
|
||||
return myLastRequest;
|
||||
}
|
||||
|
||||
public HttpResponse getLastResponse() {
|
||||
return myLastResponse;
|
||||
}
|
||||
|
||||
public String getLastResponseBody() {
|
||||
return myResponseBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptRequest(HttpRequestBase theRequest) {
|
||||
assert myLastRequest == null;
|
||||
myLastRequest = theRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptResponse(HttpResponse theResponse) throws IOException {
|
||||
assert myLastResponse == null;
|
||||
myLastResponse = theResponse;
|
||||
|
||||
HttpEntity respEntity = theResponse.getEntity();
|
||||
if (respEntity != null) {
|
||||
final byte[] bytes;
|
||||
try {
|
||||
bytes = IOUtils.toByteArray(respEntity.getContent());
|
||||
} catch (IllegalStateException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
myResponseBody = new String(bytes, "UTF-8");
|
||||
theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyEntityWrapper extends HttpEntityWrapper {
|
||||
|
||||
private byte[] myBytes;
|
||||
|
||||
public MyEntityWrapper(HttpEntity theWrappedEntity, byte[] theBytes) {
|
||||
super(theWrappedEntity);
|
||||
myBytes = theBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getContent() throws IOException {
|
||||
return new ByteArrayInputStream(myBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream theOutstream) throws IOException {
|
||||
theOutstream.write(myBytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private enum ResultType {
|
||||
BUNDLE, NONE, RESOURCE, TAGLIST
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue