More qwork on the tester

This commit is contained in:
jamesagnew 2014-04-29 17:29:46 -04:00
parent c20e7e450a
commit 7736e201e2
35 changed files with 1600 additions and 169 deletions

View File

@ -36,8 +36,10 @@ import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.JsonParser;
import ca.uhn.fhir.parser.XmlParser;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.IRestfulClientFactory;
import ca.uhn.fhir.rest.client.RestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
/**
@ -82,8 +84,7 @@ public class FhirContext {
}
/**
* Returns the scanned runtime model for the given type. This is an advanced feature
* which is generally only needed for extending the core library.
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library.
*/
public BaseRuntimeElementDefinition<?> getElementDefinition(Class<? extends IElement> theElementType) {
return myClassToElementDefinition.get(theElementType);
@ -94,8 +95,7 @@ public class FhirContext {
}
/**
* Returns the scanned runtime model for the given type. This is an advanced feature
* which is generally only needed for extending the core library.
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library.
*/
public RuntimeResourceDefinition getResourceDefinition(Class<? extends IResource> theResourceType) {
RuntimeResourceDefinition retVal = (RuntimeResourceDefinition) myClassToElementDefinition.get(theResourceType);
@ -106,21 +106,19 @@ public class FhirContext {
}
/**
* Returns the scanned runtime model for the given type. This is an advanced feature
* which is generally only needed for extending the core library.
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library.
*/
public RuntimeResourceDefinition getResourceDefinition(IResource theResource) {
return getResourceDefinition(theResource.getClass());
}
/**
* Returns the scanned runtime model for the given type. This is an advanced feature
* which is generally only needed for extending the core library.
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library.
*/
@SuppressWarnings("unchecked")
public RuntimeResourceDefinition getResourceDefinition(String theResourceName) {
Validate.notBlank(theResourceName, "Resource name must not be blank");
RuntimeResourceDefinition retVal = myNameToElementDefinition.get(theResourceName);
if (retVal == null) {
@ -139,16 +137,14 @@ public class FhirContext {
}
/**
* Returns the scanned runtime model for the given type. This is an advanced feature
* which is generally only needed for extending the core library.
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library.
*/
public RuntimeResourceDefinition getResourceDefinitionById(String theId) {
return myIdToResourceDefinition.get(theId);
}
/**
* Returns the scanned runtime models. This is an advanced feature
* which is generally only needed for extending the core library.
* Returns the scanned runtime models. This is an advanced feature which is generally only needed for extending the core library.
*/
public Collection<RuntimeResourceDefinition> getResourceDefinitions() {
return myIdToResourceDefinition.values();
@ -166,11 +162,10 @@ public class FhirContext {
}
/**
* Create and return a new JSON parser.
* Create and return a new JSON parser.
*
* <p>
* Performance Note: <b>This class is cheap</b> to create, and may be called once for
* every message being processed without incurring any performance penalty
* Performance Note: <b>This class is cheap</b> to create, and may be called once for every message being processed without incurring any performance penalty
* </p>
*/
public IParser newJsonParser() {
@ -178,11 +173,12 @@ public class FhirContext {
}
/**
* Instantiates a new client instance.
* Instantiates a new client instance. This method requires an interface which is defined specifically for your use cases to contain methods for each of the RESTful operations you wish to
* implement (e.g. "read ImagingStudy", "search Patient by identifier", etc.). This interface must extend {@link IRestfulClient} (or commonly its sub-interface {@link IBasicClient}). See the <a
* href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html">RESTful Client</a> documentation for more information on how to define this interface.
*
* <p>
* Performance Note: <b>This class is cheap</b> to create, and may be called once for
* every message being processed without incurring any performance penalty
* Performance Note: <b>This class is cheap</b> to create, and may be called once for every message being processed without incurring any performance penalty
* </p>
*
* @param theClientType
@ -198,11 +194,25 @@ public class FhirContext {
}
/**
* Create and return a new JSON parser.
* Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against a compliant server, but does not have methods defining the specific
* functionality required (as is the case with {@link #newRestfulClient(Class, String) non-generic clients}).
* <p>
* In most cases it is preferable to use the non-generic clients instead of this mechanism, but not always.
* </p>
*
* @param theServerBase
* The URL of the base for the restful FHIR server to connect to
* @return
*/
public IGenericClient newRestfulGenericClient(String theServerBase) {
return getRestfulClientFactory().newGenericClient(theServerBase);
}
/**
* Create and return a new JSON parser.
*
* <p>
* Performance Note: <b>This class is cheap</b> to create, and may be called once for
* every message being processed without incurring any performance penalty
* Performance Note: <b>This class is cheap</b> to create, and may be called once for every message being processed without incurring any performance penalty
* </p>
*/
public IParser newXmlParser() {

View File

@ -32,7 +32,7 @@ import org.apache.http.client.methods.HttpGet;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.ClientInvocationHandler;
import ca.uhn.fhir.rest.client.BaseClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
public abstract class BaseResourceReference extends BaseElement {
@ -127,7 +127,7 @@ public abstract class BaseResourceReference extends BaseElement {
// TODO: choose appropriate parser based on response CT
IParser parser = context.newXmlParser();
Reader responseReader = ClientInvocationHandler.createReaderFromResponse(response);
Reader responseReader = BaseClient.createReaderFromResponse(response);
myResource = parser.parseResource(responseReader);
} finally {

View File

@ -0,0 +1,171 @@
package ca.uhn.fhir.rest.client;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.method.IClientResponseHandler;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
public abstract class BaseClient {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseClient.class);
private final HttpClient myClient;
private boolean myKeepResponses = false;
private HttpResponse myLastResponse;
private String myLastResponseBody;
private final String myUrlBase;
BaseClient(HttpClient theClient, String theUrlBase) {
super();
myClient = theClient;
myUrlBase = theUrlBase;
}
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public HttpResponse getLastResponse() {
return myLastResponse;
}
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public String getLastResponseBody() {
return myLastResponseBody;
}
String getServerBase() {
return myUrlBase;
}
Object invokeClient(IClientResponseHandler binding, BaseClientInvocation clientInvocation) {
// TODO: handle non 2xx status codes by throwing the correct exception,
// and ensure it's passed upwards
HttpRequestBase httpRequest;
HttpResponse response;
try {
httpRequest = clientInvocation.asHttpRequest(myUrlBase);
response = myClient.execute(httpRequest);
} catch (DataFormatException e) {
throw new FhirClientConnectionException(e);
} catch (IOException e) {
throw new FhirClientConnectionException(e);
}
if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) {
throw BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());
}
try {
Reader reader = createReaderFromResponse(response);
if (ourLog.isTraceEnabled() || myKeepResponses) {
String responseString = IOUtils.toString(reader);
if (myKeepResponses) {
myLastResponse = response;
myLastResponseBody = responseString;
}
ourLog.trace("FHIR response:\n{}\n{}", response, responseString);
reader = new StringReader(responseString);
}
ContentType ct = ContentType.get(response.getEntity());
String mimeType = ct.getMimeType();
Map<String, List<String>> headers = new HashMap<String, List<String>>();
if (response.getAllHeaders() != null) {
for (Header next : response.getAllHeaders()) {
String name = next.getName().toLowerCase();
List<String> list = headers.get(name);
if (list == null) {
list = new ArrayList<String>();
headers.put(name, list);
}
list.add(next.getValue());
}
}
return binding.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers);
} catch (IllegalStateException e) {
throw new FhirClientConnectionException(e);
} catch (IOException e) {
throw new FhirClientConnectionException(e);
} finally {
if (response instanceof CloseableHttpResponse) {
try {
((CloseableHttpResponse) response).close();
} catch (IOException e) {
ourLog.debug("Failed to close response", e);
}
}
}
}
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public boolean isKeepResponses() {
return myKeepResponses;
}
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public void setKeepResponses(boolean theKeepResponses) {
myKeepResponses = theKeepResponses;
}
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public void setLastResponse(HttpResponse theLastResponse) {
myLastResponse = theLastResponse;
}
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public void setLastResponseBody(String theLastResponseBody) {
myLastResponseBody = theLastResponseBody;
}
public static Reader createReaderFromResponse(HttpResponse theResponse) throws IllegalStateException, IOException {
HttpEntity entity = theResponse.getEntity();
if (entity == null) {
return new StringReader("");
}
Charset charset = null;
if (entity.getContentType().getElements() != null) {
ContentType ct = ContentType.get(entity);
charset = ct.getCharset();
}
if (charset == null) {
ourLog.warn("Response did not specify a charset.");
charset = Charset.forName("UTF-8");
}
Reader reader = new InputStreamReader(theResponse.getEntity().getContent(), charset);
return reader;
}
}

View File

@ -20,44 +20,26 @@ package ca.uhn.fhir.rest.client;
* #L%
*/
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
public class ClientInvocationHandler implements InvocationHandler {
public class ClientInvocationHandler extends BaseClient implements InvocationHandler {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ClientInvocationHandler.class);
private final Map<Method, BaseMethodBinding> myBindings = new HashMap<Method, BaseMethodBinding>();
private final HttpClient myClient;
private final String myUrlBase;
private final Map<Method, Object> myMethodToReturnValue = new HashMap<Method, Object>();
public ClientInvocationHandler(HttpClient theClient, FhirContext theContext, String theUrlBase, Class<? extends IRestfulClient> theClientType) {
myClient = theClient;
myUrlBase = theUrlBase;
super(theClient, theUrlBase);
try {
myMethodToReturnValue.put(theClientType.getMethod("getFhirContext"), theContext);
myMethodToReturnValue.put(theClientType.getMethod("getHttpClient"), theClient);
@ -82,62 +64,10 @@ public class ClientInvocationHandler implements InvocationHandler {
BaseMethodBinding binding = myBindings.get(theMethod);
BaseClientInvocation clientInvocation = binding.invokeClient(theArgs);
HttpRequestBase httpRequest = clientInvocation.asHttpRequest(myUrlBase);
HttpResponse response = myClient.execute(httpRequest);
try {
Reader reader = createReaderFromResponse(response);
if (ourLog.isTraceEnabled()) {
String responseString = IOUtils.toString(reader);
ourLog.trace("FHIR response:\n{}\n{}", response, responseString);
reader = new StringReader(responseString);
}
ContentType ct = ContentType.get(response.getEntity());
String mimeType = ct.getMimeType();
Map<String, List<String>> headers = new HashMap<String, List<String>>();
if (response.getAllHeaders() != null) {
for (Header next : response.getAllHeaders()) {
String name = next.getName().toLowerCase();
List<String> list = headers.get(name);
if (list == null) {
list = new ArrayList<String>();
headers.put(name, list);
}
list.add(next.getValue());
}
}
return binding.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers);
} finally {
if (response instanceof CloseableHttpResponse) {
((CloseableHttpResponse) response).close();
}
}
return invokeClient(binding, clientInvocation);
}
public static Reader createReaderFromResponse(HttpResponse theResponse) throws IllegalStateException, IOException {
HttpEntity entity = theResponse.getEntity();
if (entity == null) {
return new StringReader("");
}
Charset charset = null;
if (entity.getContentType().getElements() != null) {
ContentType ct = ContentType.get(entity);
charset = ct.getCharset();
}
if (charset == null) {
ourLog.warn("Response did not specify a charset.");
charset = Charset.forName("UTF-8");
}
Reader reader = new InputStreamReader(theResponse.getEntity().getContent(), charset);
return reader;
}
}

View File

@ -0,0 +1,92 @@
package ca.uhn.fhir.rest.client;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.method.IClientResponseHandler;
import ca.uhn.fhir.rest.method.ReadMethodBinding;
import ca.uhn.fhir.rest.server.EncodingUtil;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
public class GenericClient extends BaseClient implements IGenericClient {
private FhirContext myContext;
private HttpRequestBase myLastRequest;
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public GenericClient(FhirContext theContext, HttpClient theHttpClient, String theServerBase) {
super(theHttpClient, theServerBase);
myContext = theContext;
}
public HttpRequestBase getLastRequest() {
return myLastRequest;
}
@Override
public <T extends IResource> T read(final Class<T> theType, IdDt theId) {
GetClientInvocation invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType));
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase());
}
IClientResponseHandler binding = new IClientResponseHandler() {
@Override
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
EncodingUtil respType = EncodingUtil.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseResource(theType, theResponseReader);
}
};
@SuppressWarnings("unchecked")
T resp = (T) invokeClient(binding, invocation);
return resp;
}
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public void setLastRequest(HttpRequestBase theLastRequest) {
myLastRequest = theLastRequest;
}
private String toResourceName(Class<? extends IResource> theType) {
return myContext.getResourceDefinition(theType).getName();
}
@Override
public <T extends IResource> T vread(final Class<T> theType, IdDt theId, IdDt theVersionId) {
GetClientInvocation invocation = ReadMethodBinding.createVReadInvocation(theId, theVersionId, toResourceName(theType));
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase());
}
IClientResponseHandler binding = new IClientResponseHandler() {
@Override
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
EncodingUtil respType = EncodingUtil.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseResource(theType, theResponseReader);
}
};
@SuppressWarnings("unchecked")
T resp = (T) invokeClient(binding, invocation);
return resp;
}
}

View File

@ -0,0 +1,27 @@
package ca.uhn.fhir.rest.client;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
public interface IGenericClient {
/**
* Implementation of the "read" method.
*
* @param theType The type of resource to load
* @param theId The ID to load
* @return The resource
*/
<T extends IResource> T read(Class<T> theType, IdDt theId);
/**
* Implementation of the "vread" method.
*
* @param theType The type of resource to load
* @param theId The ID to load
* @param theVersionId The version ID
* @return The resource
*/
<T extends IResource> T vread(Class<T> theType, IdDt theId, IdDt theVersionId);
}

View File

@ -57,4 +57,13 @@ public interface IRestfulClientFactory {
*/
HttpClient getHttpClient();
/**
* Instantiates a new generic client instance
*
* @param theServerBase
* The URL of the base for the restful FHIR server to connect to
* @return A newly created client
*/
IGenericClient newGenericClient(String theServerBase);
}

View File

@ -54,7 +54,7 @@ public class RestfulClientFactory implements IRestfulClientFactory {
@SuppressWarnings("unchecked")
private <T extends IRestfulClient> T instantiateProxy(Class<T> theClientType, InvocationHandler theInvocationHandler) {
T proxy = (T) Proxy.newProxyInstance(RestfulClientFactory.class.getClassLoader(), new Class[] { theClientType }, theInvocationHandler);
T proxy = (T) Proxy.newProxyInstance(theClientType.getClassLoader(), new Class[] { theClientType }, theInvocationHandler);
return proxy;
}
@ -120,4 +120,9 @@ public class RestfulClientFactory implements IRestfulClientFactory {
myHttpClient = theHttpClient;
}
@Override
public IGenericClient newGenericClient(String theServerBase) {
return new GenericClient(myContext, getHttpClient(), theServerBase);
}
}

View File

@ -0,0 +1,14 @@
package ca.uhn.fhir.rest.client.exceptions;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
public class FhirClientConnectionException extends BaseServerResponseException {
private static final long serialVersionUID = 1L;
public FhirClientConnectionException(Throwable theE) {
super(0, theE);
}
}

View File

@ -65,7 +65,7 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.ReflectionUtil;
public abstract class BaseMethodBinding {
public abstract class BaseMethodBinding implements IClientResponseHandler {
private FhirContext myContext;
private Method myMethod;
@ -106,8 +106,6 @@ public abstract class BaseMethodBinding {
public abstract BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException;
public abstract Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException;
public abstract void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException;
public abstract boolean matches(Request theRequest);

View File

@ -0,0 +1,14 @@
package ca.uhn.fhir.rest.method;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
public interface IClientResponseHandler {
Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException;
}

View File

@ -118,15 +118,25 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
@Override
public GetClientInvocation invokeClient(Object[] theArgs) {
String id = ((IdDt) theArgs[myIdIndex]).getValue();
IdDt id = ((IdDt) theArgs[myIdIndex]);
if (myVersionIdIndex == null) {
return new GetClientInvocation(getResourceName(), id);
String resourceName = getResourceName();
return createReadInvocation(id, resourceName);
} else {
String vid = ((IdDt) theArgs[myVersionIdIndex]).getValue();
return new GetClientInvocation(getResourceName(), id, Constants.URL_TOKEN_HISTORY, vid);
IdDt vid = ((IdDt) theArgs[myVersionIdIndex]);
String resourceName = getResourceName();
return createVReadInvocation(id, vid, resourceName);
}
}
public static GetClientInvocation createVReadInvocation(IdDt theId, IdDt vid, String resourceName) {
return new GetClientInvocation(resourceName, theId.getValue(), Constants.URL_TOKEN_HISTORY, vid.getValue());
}
public static GetClientInvocation createReadInvocation(IdDt theId, String resourceName) {
return new GetClientInvocation(resourceName, theId.getValue());
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return isVread() ? RestfulOperationTypeEnum.VREAD : RestfulOperationTypeEnum.READ;

View File

@ -60,6 +60,8 @@ public class Constants {
public static final String HEADER_LAST_MODIFIED = "Last-Modified";
public static final String HEADER_LAST_MODIFIED_LOWERCASE = HEADER_LAST_MODIFIED.toLowerCase();
public static final String PARAM_VALIDATE = "_validate";
public static final int STATUS_HTTP_401_CLIENT_UNAUTHORIZED = 401;
public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500;
static {
Map<String, EncodingUtil> valToEncoding = new HashMap<String, EncodingUtil>();

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.rest.server.exceptions;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/*
* #%L
@ -29,14 +29,16 @@ import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
*/
public class AuthenticationException extends BaseServerResponseException {
private static final long serialVersionUID = 1L;
public static final int STATUS_CODE = Constants.STATUS_HTTP_401_CLIENT_UNAUTHORIZED;
private static final long serialVersionUID = 1L;
public AuthenticationException() {
super(401, "Client unauthorized");
super(STATUS_CODE, "Client unauthorized");
}
public AuthenticationException(String theMessage) {
super(401, theMessage);
super(STATUS_CODE, theMessage);
}
}

View File

@ -1,5 +1,9 @@
package ca.uhn.fhir.rest.server.exceptions;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
/*
@ -24,11 +28,23 @@ import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
public abstract class BaseServerResponseException extends RuntimeException {
private static final Map<Integer, Class<? extends BaseServerResponseException>> ourStatusCodeToExceptionType = new HashMap<Integer, Class<? extends BaseServerResponseException>>();
private static final long serialVersionUID = 1L;
private int myStatusCode;
static {
registerExceptionType(AuthenticationException.STATUS_CODE, AuthenticationException.class);
registerExceptionType(InternalErrorException.STATUS_CODE, InternalErrorException.class);
registerExceptionType(InvalidRequestException.STATUS_CODE, InvalidRequestException.class);
registerExceptionType(MethodNotAllowedException.STATUS_CODE, MethodNotAllowedException.class);
registerExceptionType(ResourceNotFoundException.STATUS_CODE, ResourceNotFoundException.class);
registerExceptionType(ResourceVersionNotSpecifiedException.STATUS_CODE, ResourceVersionNotSpecifiedException.class);
registerExceptionType(UnprocessableEntityException.STATUS_CODE, UnprocessableEntityException.class);
}
private final OperationOutcome myOperationOutcome;
private int myStatusCode;
/**
* Constructor
*
@ -40,7 +56,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
public BaseServerResponseException(int theStatusCode, String theMessage) {
super(theMessage);
myStatusCode = theStatusCode;
myOperationOutcome=null;
myOperationOutcome = null;
}
/**
@ -54,9 +70,9 @@ public abstract class BaseServerResponseException extends RuntimeException {
public BaseServerResponseException(int theStatusCode, String theMessage, OperationOutcome theOperationOutcome) {
super(theMessage);
myStatusCode = theStatusCode;
myOperationOutcome=theOperationOutcome;
myOperationOutcome = theOperationOutcome;
}
/**
* Constructor
*
@ -64,12 +80,13 @@ public abstract class BaseServerResponseException extends RuntimeException {
* The HTTP status code corresponding to this problem
* @param theMessage
* The message
* @param theCause The cause
* @param theCause
* The cause
*/
public BaseServerResponseException(int theStatusCode, String theMessage, Throwable theCause) {
super(theMessage, theCause);
myStatusCode = theStatusCode;
myOperationOutcome=null;
myOperationOutcome = null;
}
/**
@ -83,7 +100,14 @@ public abstract class BaseServerResponseException extends RuntimeException {
public BaseServerResponseException(int theStatusCode, Throwable theCause) {
super(theCause.toString(), theCause);
myStatusCode = theStatusCode;
myOperationOutcome=null;
myOperationOutcome = null;
}
/**
* Returns the {@link OperationOutcome} resource if any which was supplied in the response, or <code>null</code>
*/
public OperationOutcome getOperationOutcome() {
return myOperationOutcome;
}
/**
@ -93,11 +117,31 @@ public abstract class BaseServerResponseException extends RuntimeException {
return myStatusCode;
}
/**
* Returns the {@link OperationOutcome} resource if any which was supplied in the response,
* or <code>null</code>
*/
public OperationOutcome getOperationOutcome() {
return myOperationOutcome;
public static BaseServerResponseException newInstance(int theStatusCode, String theMessage) {
if (ourStatusCodeToExceptionType.containsKey(theStatusCode)) {
try {
return ourStatusCodeToExceptionType.get(theStatusCode).getConstructor(new Class[] { String.class }).newInstance(theMessage);
} catch (InstantiationException e) {
throw new InternalErrorException(e);
} catch (IllegalAccessException e) {
throw new InternalErrorException(e);
} catch (IllegalArgumentException e) {
throw new InternalErrorException(e);
} catch (InvocationTargetException e) {
throw new InternalErrorException(e);
} catch (NoSuchMethodException e) {
throw new InternalErrorException(e);
} catch (SecurityException e) {
throw new InternalErrorException(e);
}
} else {
return new UnclassifiedServerFailureException(theStatusCode, theMessage);
}
}
static void registerExceptionType(int theStatusCode, Class<? extends BaseServerResponseException> theType) {
if (ourStatusCodeToExceptionType.containsKey(theStatusCode)) {
throw new Error("Can not register " + theType + " to status code " + theStatusCode + " because " + ourStatusCodeToExceptionType.get(theStatusCode) + " already registers that code");
}
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server.exceptions;
import ca.uhn.fhir.rest.server.Constants;
/*
* #%L
* HAPI FHIR Library
@ -25,18 +27,20 @@ package ca.uhn.fhir.rest.server.exceptions;
*/
public class InternalErrorException extends BaseServerResponseException {
public static final int STATUS_CODE = Constants.STATUS_HTTP_500_INTERNAL_ERROR;
private static final long serialVersionUID = 1L;
public InternalErrorException(String theMessage) {
super(500, theMessage);
super(STATUS_CODE, theMessage);
}
public InternalErrorException(String theMessage, Throwable theCause) {
super(500, theMessage, theCause);
super(STATUS_CODE, theMessage, theCause);
}
public InternalErrorException(Throwable theCause) {
super(500, theCause);
super(STATUS_CODE, theCause);
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server.exceptions;
import ca.uhn.fhir.rest.server.Constants;
/*
* #%L
* HAPI FHIR Library
@ -30,10 +32,11 @@ package ca.uhn.fhir.rest.server.exceptions;
*/
public class InvalidRequestException extends BaseServerResponseException {
public static final int STATUS_CODE = Constants.STATUS_HTTP_400_BAD_REQUEST;
private static final long serialVersionUID = 1L;
public InvalidRequestException(String theMessage) {
super(400, theMessage);
super(STATUS_CODE, theMessage);
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server.exceptions;
import ca.uhn.fhir.rest.server.Constants;
/*
* #%L
* HAPI FHIR Library
@ -24,9 +26,10 @@ package ca.uhn.fhir.rest.server.exceptions;
* TODO: javadoc this
*/
public class MethodNotAllowedException extends BaseServerResponseException {
public static final int STATUS_CODE = Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED;
private static final long serialVersionUID = 1L;
public MethodNotAllowedException(String error) {
super(405, error);
super(STATUS_CODE, error);
}
}

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.rest.server.exceptions;
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 <b>HTTP 404 Resource Not Found</b> response, which means that
@ -30,16 +31,18 @@ import ca.uhn.fhir.model.primitive.IdDt;
*/
public class ResourceNotFoundException extends BaseServerResponseException {
public static final int STATUS_CODE = Constants.STATUS_HTTP_404_NOT_FOUND;
public ResourceNotFoundException(IdDt theId) {
super(404, "Resource " + (theId != null ? theId.getValue() : "") + " is not known");
super(STATUS_CODE, "Resource " + (theId != null ? theId.getValue() : "") + " is not known");
}
public ResourceNotFoundException(Class<? extends IResource> theClass, IdentifierDt thePatientId) {
super(404, "Resource of type " + theClass.getSimpleName() + " with ID " + thePatientId + " is not known");
super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + thePatientId + " is not known");
}
public ResourceNotFoundException(String theMessage) {
super(404, theMessage);
super(STATUS_CODE, theMessage);
}
private static final long serialVersionUID = 1L;

View File

@ -30,9 +30,10 @@ import ca.uhn.fhir.rest.server.Constants;
* when the operation fails because of a version conflict as specified in the FHIR specification.
*/
public class ResourceVersionConflictException extends BaseServerResponseException {
public static final int STATUS_CODE = Constants.STATUS_HTTP_409_CONFLICT;
private static final long serialVersionUID = 1L;
public ResourceVersionConflictException(String error) {
super(Constants.STATUS_HTTP_409_CONFLICT, error);
super(STATUS_CODE, error);
}
}

View File

@ -29,9 +29,10 @@ import ca.uhn.fhir.rest.server.Constants;
* be specified in an HTTP header, and none was.
*/
public class ResourceVersionNotSpecifiedException extends BaseServerResponseException {
public static final int STATUS_CODE = Constants.STATUS_HTTP_412_PRECONDITION_FAILED;
private static final long serialVersionUID = 1L;
public ResourceVersionNotSpecifiedException(String error) {
super(Constants.STATUS_HTTP_412_PRECONDITION_FAILED, error);
super(STATUS_CODE, error);
}
}

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.rest.server.exceptions;
*/
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/**
* Represents an <b>HTTP 422 Unprocessable Entity</b> response, which means that a resource was rejected by the server because it "violated applicable FHIR profiles or server business rules".
@ -35,26 +36,27 @@ public class UnprocessableEntityException extends BaseServerResponseException {
private static final String DEFAULT_MESSAGE = "Unprocessable Entity";
private static final long serialVersionUID = 1L;
public static final int STATUS_CODE = Constants.STATUS_HTTP_422_UNPROCESSABLE_ENTITY;
/**
* Constructor which accepts an {@link OperationOutcome} resource which will be supplied in the response
*/
public UnprocessableEntityException(OperationOutcome theOperationOutcome) {
super(422, DEFAULT_MESSAGE, theOperationOutcome == null ? new OperationOutcome() : theOperationOutcome);
super(STATUS_CODE, DEFAULT_MESSAGE, theOperationOutcome == null ? new OperationOutcome() : theOperationOutcome);
}
/**
* Constructor which accepts a String describing the issue. This string will be translated into an {@link OperationOutcome} resource which will be supplied in the response.
*/
public UnprocessableEntityException(String theMessage) {
super(422, DEFAULT_MESSAGE, toOperationOutcome(theMessage));
super(STATUS_CODE, DEFAULT_MESSAGE, toOperationOutcome(theMessage));
}
/**
* Constructor which accepts an array of Strings describing the issue. This strings will be translated into an {@link OperationOutcome} resource which will be supplied in the response.
*/
public UnprocessableEntityException(String... theMessage) {
super(422, DEFAULT_MESSAGE, toOperationOutcome(theMessage));
super(STATUS_CODE, DEFAULT_MESSAGE, toOperationOutcome(theMessage));
}
private static OperationOutcome toOperationOutcome(String... theMessage) {

View File

@ -11,6 +11,10 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
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;
@ -19,14 +23,21 @@ 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.dstu.resource.Conformance;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.client.GenericClient;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingUtil;
public class PublicTesterServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PublicTesterServlet.class);
private static final boolean DEBUGMODE = true;
private TemplateEngine myTemplateEngine;
private HashMap<String, String> myStaticResources;
private String myServerBase;
@ -43,38 +54,137 @@ public class PublicTesterServlet extends HttpServlet {
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");
myCtx = new FhirContext();
}
@Override
protected void doPost(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
if (DEBUGMODE) {
myTemplateEngine.getCacheManager().clearAllCaches();
}
try {
GenericClient client = (GenericClient) myCtx.newRestfulGenericClient(myServerBase);
client.setKeepResponses(true);
String method = theReq.getParameter("method");
String requestUrl;
String action;
String resultStatus;
String resultBody;
String resultSyntaxHighlighterClass;
if ("read".equals(method)) {
String resourceName = StringUtils.defaultString(theReq.getParameter("resourceName"));
RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName);
if (def == null) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "Invalid resourceName: " + resourceName);
return;
}
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
client.read(def.getImplementingClass(), new IdDt(id));
} if ("vread".equals(method)) {
String resourceName = StringUtils.defaultString(theReq.getParameter("resourceName"));
RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName);
if (def == null) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "Invalid resourceName: " + resourceName);
return;
}
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");
}
client.vread(def.getImplementingClass(), new IdDt(id), new IdDt(versionId));
} else {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "Invalid method: " + method);
return;
}
HttpRequestBase lastRequest = client.getLastRequest();
requestUrl = lastRequest.getURI().toASCIIString();
action = client.getLastRequest().getMethod();
resultStatus = client.getLastResponse().getStatusLine().toString();
resultBody = client.getLastResponseBody();
ContentType ct = ContentType.get(client.getLastResponse().getEntity());
String mimeType = ct.getMimeType();
EncodingUtil ctEnum = EncodingUtil.forContentType(mimeType);
switch (ctEnum) {
case JSON:
resultSyntaxHighlighterClass="brush: jscript";
break;
case XML:
resultSyntaxHighlighterClass="brush: xml";
break;
default:
resultSyntaxHighlighterClass="brush: plain";
break;
}
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("resultBody", StringEscapeUtils.escapeHtml(resultBody));
ctx.setVariable("resultSyntaxHighlighterClass", resultSyntaxHighlighterClass);
myTemplateEngine.process(PUBLIC_TESTER_RESULT_HTML, ctx, theResp.getWriter());
} catch (Exception e) {
ourLog.error("Failure during processing", e);
theResp.sendError(500, e.toString());
}
}
@Override
protected void doGet(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
if (DEBUGMODE) {
myTemplateEngine.getCacheManager().clearAllCaches();
ourLog.info("RequestURI: {}", theReq.getPathInfo());
}
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);
myTemplateEngine.process(theReq.getPathInfo(), ctx, theResp.getWriter());
ctx.setVariable("jsonEncodedConf", myCtx.newJsonParser().encodeResourceToString(conformance));
myTemplateEngine.process(theReq.getPathInfo(), ctx, theResp.getWriter());
}
private interface ConformanceClient extends IBasicClient
{
private interface ConformanceClient extends IBasicClient {
@Metadata
Conformance getConformance();
}
@Override
public void init(ServletConfig theConfig) throws ServletException {
myTemplateEngine = new TemplateEngine();
@ -86,7 +196,10 @@ public class PublicTesterServlet extends HttpServlet {
myTemplateEngine.initialize();
}
private static final String PUBLIC_TESTER_RESULT_HTML = "/PublicTesterResult.html";
private final class ProfileResourceResolver implements IResourceResolver {
@Override
public String getName() {
return getClass().getCanonicalName();
@ -94,12 +207,15 @@ public class PublicTesterServlet extends HttpServlet {
@Override
public InputStream getResourceAsStream(TemplateProcessingParameters theTemplateProcessingParameters, String theName) {
ourLog.info("Loading template: {}", theName);
ourLog.debug("Loading template: {}", theName);
if ("/".equals(theName)) {
return PublicTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTester.html");
}
if (PUBLIC_TESTER_RESULT_HTML.equals(theName)) {
return PublicTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTesterResult.html");
}
return null;
return null;
}
}

View File

@ -19,4 +19,8 @@ TD.testerNameCell {
background-color: #E0FFE0;
border-radius: 3px;
padding: 3px;
}
}
.syntaxhighlighter {
font-size: 0.85em !important;
}

View File

@ -10,7 +10,8 @@ This file is a Thymeleaf template for the
<script type="text/javascript" src="jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="PublicTester.js"></script>
<script type="text/javascript">
var serverBase = "<th:block th:text="${base}"/>";
var serverBase = "<th:block th:text="${base}"/>";
var conformance = <th:block th:text="${jsonEncodedConf}"/>;
</script>
<link rel="stylesheet" type="text/css" href="PublicTester.css"/>
</head>
@ -62,7 +63,8 @@ This file is a Thymeleaf template for the
<td valign="top">
<th:span th:each="operation : ${resource.operation}">
<th:block th:switch="${operation.code.value}">
<a th:case="'read'" th:href="'javascript:displayRead(\'' + ${expandoId} + '\');'">read</a>
<a th:case="'read'" th:href="'javascript:displayRead(\'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\');'">read</a>
<a th:case="'vread'" th:href="'javascript:displayVRead(\'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\');'">vread</a>
<span th:case="*" th:text="${operation.code.value}"/>
</th:block>
</th:span>

View File

@ -1,20 +1,60 @@
var currentForm;
/** Hide any currently displayed tester form */
function clearCurrentForm(postCompleteFunction) {
if (currentForm != null) {
var holder = currentForm;
holder.children().fadeOut(500).promise().then(function() {
holder.empty();
holder.hide();
postCompleteFunction();
});
currentForm = null;
} else {
postCompleteFunction();
}
}
/** Create a tester form for the 'read' method */
function displayRead(expandoTr) {
$('#' + expandoTr).show();
currentForm = $('#' + expandoTr).append(
$('<td class="testerNameCell">Read</td>'),
$('<td />').append(
$('<form />', { action: '', method: 'POST' }).append(
$('<input />', { name: 'method', value: 'read', type: 'hidden' }),
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
$('<br />'),
$('<input />', { type: 'submit', value: 'Submit' })
)
)
);
function displayRead(expandoTr, resourceName) {
var postCompleteFunction = function() {
//$('#' + expandoTr).show();
currentForm = $('#' + expandoTr).append(
$('<td class="testerNameCell">Read</td>'),
$('<td />').append(
$('<form/>', { id: 'testerform', action: 'PublicTesterResult.html', method: 'POST' }).append(
$('<input />', { name: 'method', value: 'read', type: 'hidden' }),
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
$('<br />'),
$('<input />', { type: 'submit', value: 'Submit' })
)
)
);
$('#' + expandoTr).fadeIn(500);
}
clearCurrentForm(postCompleteFunction);
}
/** Create a tester form for the 'read' method */
function displayVRead(expandoTr, resourceName) {
var postCompleteFunction = function() {
//$('#' + expandoTr).show();
currentForm = $('#' + expandoTr).append(
$('<td class="testerNameCell">VRead</td>'),
$('<td />').append(
$('<form/>', { id: 'testerform', action: 'PublicTesterResult.html', method: 'POST' }).append(
$('<input />', { name: 'method', value: 'vread', type: 'hidden' }),
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
$('<input />', { name: 'versionid', placeholder: 'Version ID', type: 'text' }),
$('<br />'),
$('<input />', { type: 'submit', value: 'Submit' })
)
)
);
$('#' + expandoTr).fadeIn(500);
}
clearCurrentForm(postCompleteFunction);
}

View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<!--/*
************************************************************
This file is a Thymeleaf template for the
************************************************************
*/-->
<html>
<head>
<script src="shCore.js" type="text/javascript"></script>
<script src="shBrushJScript.js" type="text/javascript"></script>
<script src="shBrushXml.js" type="text/javascript"></script>
<script src="shBrushPlain.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="shCore.css"/>
<link rel="stylesheet" type="text/css" href="shThemeDefault.css"/>
<script type="text/javascript" src="jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="PublicTester.js"></script>
<script type="text/javascript">
var serverBase = "<th:block th:utext="${base}"/>";
</script>
<link rel="stylesheet" type="text/css" href="PublicTester.css"/>
</head>
<body>
<table border="0" width="100%">
<tr>
<td align="left"><img src="hapi_fhir_banner.png"/></td>
<td align="right"><img src="hapi_fhir_banner_right.png"/></td>
</tr>
</table>
<div class="bodyHeaderBlock">
Executed invocation:<br/>
<th:block th:text="${action}">GET</th:block>
<a href="http://foo.com/fhir" th:href="${requestUrl}"><th:block th:text="${requestUrl}">http://foo.com/fhir</th:block></a>
</div>
<table border="0" width="100%" cellpadding="0" cellspacing="0" style="margin-top: 4px;">
<tr>
<td width="29%" valign="top">
<div class="bodyHeaderBlock">
Actions
</div>
<table border="0">
<tr>
<td class="propertyKeyCell">Something</td>
<td>Something</td>
</tr>
</table>
</td>
<td width="1%"/>
<td width="70%" valign="top">
<div class="bodyHeaderBlock">
Response
</div>
<table border="0">
<tr>
<td valign="top" class="propertyKeyCell">Status</td>
<td valign="top" th:text="${resultStatus}">HTTP 200 OK</td>
</tr>
<tr>
<td valign="top" class="propertyKeyCell">Result</td>
<td valign="top"><pre th:text="${resultBody}" th:class="${resultSyntaxHighlighterClass}">{}</pre></td>
</tr>
</table>
<div>
</div>
</td>
</tr>
</table>
<script type="text/javascript">
SyntaxHighlighter.all();
</script>
</body>
</html>

View File

@ -0,0 +1,52 @@
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
;(function()
{
// CommonJS
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
function Brush()
{
var keywords = 'break case catch continue ' +
'default delete do else false ' +
'for function if in instanceof ' +
'new null return super switch ' +
'this throw true try typeof var while with'
;
var r = SyntaxHighlighter.regexLib;
this.regexList = [
{ regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings
{ regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings
{ regex: r.singleLineCComments, css: 'comments' }, // one line comments
{ regex: r.multiLineCComments, css: 'comments' }, // multiline comments
{ regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // keywords
];
this.forHtmlScript(r.scriptScriptTags);
};
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['js', 'jscript', 'javascript'];
SyntaxHighlighter.brushes.JScript = Brush;
// CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})();

View File

@ -0,0 +1,33 @@
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
;(function()
{
// CommonJS
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
function Brush()
{
};
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['text', 'plain'];
SyntaxHighlighter.brushes.Plain = Brush;
// CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})();

View File

@ -0,0 +1,69 @@
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
;(function()
{
// CommonJS
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
function Brush()
{
function process(match, regexInfo)
{
var constructor = SyntaxHighlighter.Match,
code = match[0],
tag = new XRegExp('(&lt;|<)[\\s\\/\\?]*(?<name>[:\\w-\\.]+)', 'xg').exec(code),
result = []
;
if (match.attributes != null)
{
var attributes,
regex = new XRegExp('(?<name> [\\w:\\-\\.]+)' +
'\\s*=\\s*' +
'(?<value> ".*?"|\'.*?\'|\\w+)',
'xg');
while ((attributes = regex.exec(code)) != null)
{
result.push(new constructor(attributes.name, match.index + attributes.index, 'color1'));
result.push(new constructor(attributes.value, match.index + attributes.index + attributes[0].indexOf(attributes.value), 'string'));
}
}
if (tag != null)
result.push(
new constructor(tag.name, match.index + tag[0].indexOf(tag.name), 'keyword')
);
return result;
}
this.regexList = [
{ regex: new XRegExp('(\\&lt;|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\&gt;|>)', 'gm'), css: 'color2' }, // <![ ... [ ... ]]>
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
{ regex: new XRegExp('(&lt;|<)[\\s\\/\\?]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(&gt;|>)', 'sg'), func: process }
];
};
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['xml', 'xhtml', 'xslt', 'html'];
SyntaxHighlighter.brushes.Xml = Brush;
// CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})();

View File

@ -0,0 +1,226 @@
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
.syntaxhighlighter a,
.syntaxhighlighter div,
.syntaxhighlighter code,
.syntaxhighlighter table,
.syntaxhighlighter table td,
.syntaxhighlighter table tr,
.syntaxhighlighter table tbody,
.syntaxhighlighter table thead,
.syntaxhighlighter table caption,
.syntaxhighlighter textarea {
-moz-border-radius: 0 0 0 0 !important;
-webkit-border-radius: 0 0 0 0 !important;
background: none !important;
border: 0 !important;
bottom: auto !important;
float: none !important;
height: auto !important;
left: auto !important;
line-height: 1.1em !important;
margin: 0 !important;
outline: 0 !important;
overflow: visible !important;
padding: 0 !important;
position: static !important;
right: auto !important;
text-align: left !important;
top: auto !important;
vertical-align: baseline !important;
width: auto !important;
box-sizing: content-box !important;
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
font-weight: normal !important;
font-style: normal !important;
font-size: 1em !important;
min-height: inherit !important;
min-height: auto !important;
}
.syntaxhighlighter {
width: 100% !important;
margin: 1em 0 1em 0 !important;
position: relative !important;
overflow: auto !important;
font-size: 1em !important;
}
.syntaxhighlighter.source {
overflow: hidden !important;
}
.syntaxhighlighter .bold {
font-weight: bold !important;
}
.syntaxhighlighter .italic {
font-style: italic !important;
}
.syntaxhighlighter .line {
white-space: pre !important;
}
.syntaxhighlighter table {
width: 100% !important;
}
.syntaxhighlighter table caption {
text-align: left !important;
padding: .5em 0 0.5em 1em !important;
}
.syntaxhighlighter table td.code {
width: 100% !important;
}
.syntaxhighlighter table td.code .container {
position: relative !important;
}
.syntaxhighlighter table td.code .container textarea {
box-sizing: border-box !important;
position: absolute !important;
left: 0 !important;
top: 0 !important;
width: 100% !important;
height: 100% !important;
border: none !important;
background: white !important;
padding-left: 1em !important;
overflow: hidden !important;
white-space: pre !important;
}
.syntaxhighlighter table td.gutter .line {
text-align: right !important;
padding: 0 0.5em 0 1em !important;
}
.syntaxhighlighter table td.code .line {
padding: 0 1em !important;
}
.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
padding-left: 0em !important;
}
.syntaxhighlighter.show {
display: block !important;
}
.syntaxhighlighter.collapsed table {
display: none !important;
}
.syntaxhighlighter.collapsed .toolbar {
padding: 0.1em 0.8em 0em 0.8em !important;
font-size: 1em !important;
position: static !important;
width: auto !important;
height: auto !important;
}
.syntaxhighlighter.collapsed .toolbar span {
display: inline !important;
margin-right: 1em !important;
}
.syntaxhighlighter.collapsed .toolbar span a {
padding: 0 !important;
display: none !important;
}
.syntaxhighlighter.collapsed .toolbar span a.expandSource {
display: inline !important;
}
.syntaxhighlighter .toolbar {
position: absolute !important;
right: 1px !important;
top: 1px !important;
width: 11px !important;
height: 11px !important;
font-size: 10px !important;
z-index: 10 !important;
}
.syntaxhighlighter .toolbar span.title {
display: inline !important;
}
.syntaxhighlighter .toolbar a {
display: block !important;
text-align: center !important;
text-decoration: none !important;
padding-top: 1px !important;
}
.syntaxhighlighter .toolbar a.expandSource {
display: none !important;
}
.syntaxhighlighter.ie {
font-size: .9em !important;
padding: 1px 0 1px 0 !important;
}
.syntaxhighlighter.ie .toolbar {
line-height: 8px !important;
}
.syntaxhighlighter.ie .toolbar a {
padding-top: 0px !important;
}
.syntaxhighlighter.printing .line.alt1 .content,
.syntaxhighlighter.printing .line.alt2 .content,
.syntaxhighlighter.printing .line.highlighted .number,
.syntaxhighlighter.printing .line.highlighted.alt1 .content,
.syntaxhighlighter.printing .line.highlighted.alt2 .content {
background: none !important;
}
.syntaxhighlighter.printing .line .number {
color: #bbbbbb !important;
}
.syntaxhighlighter.printing .line .content {
color: black !important;
}
.syntaxhighlighter.printing .toolbar {
display: none !important;
}
.syntaxhighlighter.printing a {
text-decoration: none !important;
}
.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
color: black !important;
}
.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
color: #008200 !important;
}
.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
color: blue !important;
}
.syntaxhighlighter.printing .keyword {
color: #006699 !important;
font-weight: bold !important;
}
.syntaxhighlighter.printing .preprocessor {
color: gray !important;
}
.syntaxhighlighter.printing .variable {
color: #aa7700 !important;
}
.syntaxhighlighter.printing .value {
color: #009900 !important;
}
.syntaxhighlighter.printing .functions {
color: #ff1493 !important;
}
.syntaxhighlighter.printing .constants {
color: #0066cc !important;
}
.syntaxhighlighter.printing .script {
font-weight: bold !important;
}
.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
color: gray !important;
}
.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
color: #ff1493 !important;
}
.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
color: red !important;
}
.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
color: black !important;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,328 @@
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
.syntaxhighlighter a,
.syntaxhighlighter div,
.syntaxhighlighter code,
.syntaxhighlighter table,
.syntaxhighlighter table td,
.syntaxhighlighter table tr,
.syntaxhighlighter table tbody,
.syntaxhighlighter table thead,
.syntaxhighlighter table caption,
.syntaxhighlighter textarea {
-moz-border-radius: 0 0 0 0 !important;
-webkit-border-radius: 0 0 0 0 !important;
background: none !important;
border: 0 !important;
bottom: auto !important;
float: none !important;
height: auto !important;
left: auto !important;
line-height: 1.1em !important;
margin: 0 !important;
outline: 0 !important;
overflow: visible !important;
padding: 0 !important;
position: static !important;
right: auto !important;
text-align: left !important;
top: auto !important;
vertical-align: baseline !important;
width: auto !important;
box-sizing: content-box !important;
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
font-weight: normal !important;
font-style: normal !important;
font-size: 1em !important;
min-height: inherit !important;
min-height: auto !important;
}
.syntaxhighlighter {
width: 100% !important;
margin: 1em 0 1em 0 !important;
position: relative !important;
overflow: auto !important;
font-size: 1em !important;
}
.syntaxhighlighter.source {
overflow: hidden !important;
}
.syntaxhighlighter .bold {
font-weight: bold !important;
}
.syntaxhighlighter .italic {
font-style: italic !important;
}
.syntaxhighlighter .line {
white-space: pre !important;
}
.syntaxhighlighter table {
width: 100% !important;
}
.syntaxhighlighter table caption {
text-align: left !important;
padding: .5em 0 0.5em 1em !important;
}
.syntaxhighlighter table td.code {
width: 100% !important;
}
.syntaxhighlighter table td.code .container {
position: relative !important;
}
.syntaxhighlighter table td.code .container textarea {
box-sizing: border-box !important;
position: absolute !important;
left: 0 !important;
top: 0 !important;
width: 100% !important;
height: 100% !important;
border: none !important;
background: white !important;
padding-left: 1em !important;
overflow: hidden !important;
white-space: pre !important;
}
.syntaxhighlighter table td.gutter .line {
text-align: right !important;
padding: 0 0.5em 0 1em !important;
}
.syntaxhighlighter table td.code .line {
padding: 0 1em !important;
}
.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
padding-left: 0em !important;
}
.syntaxhighlighter.show {
display: block !important;
}
.syntaxhighlighter.collapsed table {
display: none !important;
}
.syntaxhighlighter.collapsed .toolbar {
padding: 0.1em 0.8em 0em 0.8em !important;
font-size: 1em !important;
position: static !important;
width: auto !important;
height: auto !important;
}
.syntaxhighlighter.collapsed .toolbar span {
display: inline !important;
margin-right: 1em !important;
}
.syntaxhighlighter.collapsed .toolbar span a {
padding: 0 !important;
display: none !important;
}
.syntaxhighlighter.collapsed .toolbar span a.expandSource {
display: inline !important;
}
.syntaxhighlighter .toolbar {
position: absolute !important;
right: 1px !important;
top: 1px !important;
width: 11px !important;
height: 11px !important;
font-size: 10px !important;
z-index: 10 !important;
}
.syntaxhighlighter .toolbar span.title {
display: inline !important;
}
.syntaxhighlighter .toolbar a {
display: block !important;
text-align: center !important;
text-decoration: none !important;
padding-top: 1px !important;
}
.syntaxhighlighter .toolbar a.expandSource {
display: none !important;
}
.syntaxhighlighter.ie {
font-size: .9em !important;
padding: 1px 0 1px 0 !important;
}
.syntaxhighlighter.ie .toolbar {
line-height: 8px !important;
}
.syntaxhighlighter.ie .toolbar a {
padding-top: 0px !important;
}
.syntaxhighlighter.printing .line.alt1 .content,
.syntaxhighlighter.printing .line.alt2 .content,
.syntaxhighlighter.printing .line.highlighted .number,
.syntaxhighlighter.printing .line.highlighted.alt1 .content,
.syntaxhighlighter.printing .line.highlighted.alt2 .content {
background: none !important;
}
.syntaxhighlighter.printing .line .number {
color: #bbbbbb !important;
}
.syntaxhighlighter.printing .line .content {
color: black !important;
}
.syntaxhighlighter.printing .toolbar {
display: none !important;
}
.syntaxhighlighter.printing a {
text-decoration: none !important;
}
.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
color: black !important;
}
.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
color: #008200 !important;
}
.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
color: blue !important;
}
.syntaxhighlighter.printing .keyword {
color: #006699 !important;
font-weight: bold !important;
}
.syntaxhighlighter.printing .preprocessor {
color: gray !important;
}
.syntaxhighlighter.printing .variable {
color: #aa7700 !important;
}
.syntaxhighlighter.printing .value {
color: #009900 !important;
}
.syntaxhighlighter.printing .functions {
color: #ff1493 !important;
}
.syntaxhighlighter.printing .constants {
color: #0066cc !important;
}
.syntaxhighlighter.printing .script {
font-weight: bold !important;
}
.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
color: gray !important;
}
.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
color: #ff1493 !important;
}
.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
color: red !important;
}
.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
color: black !important;
}
.syntaxhighlighter {
background-color: white !important;
}
.syntaxhighlighter .line.alt1 {
background-color: white !important;
}
.syntaxhighlighter .line.alt2 {
background-color: white !important;
}
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
background-color: #e0e0e0 !important;
}
.syntaxhighlighter .line.highlighted.number {
color: black !important;
}
.syntaxhighlighter table caption {
color: black !important;
}
.syntaxhighlighter .gutter {
color: #afafaf !important;
}
.syntaxhighlighter .gutter .line {
border-right: 3px solid #6ce26c !important;
}
.syntaxhighlighter .gutter .line.highlighted {
background-color: #6ce26c !important;
color: white !important;
}
.syntaxhighlighter.printing .line .content {
border: none !important;
}
.syntaxhighlighter.collapsed {
overflow: visible !important;
}
.syntaxhighlighter.collapsed .toolbar {
color: blue !important;
background: white !important;
border: 1px solid #6ce26c !important;
}
.syntaxhighlighter.collapsed .toolbar a {
color: blue !important;
}
.syntaxhighlighter.collapsed .toolbar a:hover {
color: red !important;
}
.syntaxhighlighter .toolbar {
color: white !important;
background: #6ce26c !important;
border: none !important;
}
.syntaxhighlighter .toolbar a {
color: white !important;
}
.syntaxhighlighter .toolbar a:hover {
color: black !important;
}
.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
color: black !important;
}
.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
color: #008200 !important;
}
.syntaxhighlighter .string, .syntaxhighlighter .string a {
color: blue !important;
}
.syntaxhighlighter .keyword {
color: #006699 !important;
}
.syntaxhighlighter .preprocessor {
color: gray !important;
}
.syntaxhighlighter .variable {
color: #aa7700 !important;
}
.syntaxhighlighter .value {
color: #009900 !important;
}
.syntaxhighlighter .functions {
color: #ff1493 !important;
}
.syntaxhighlighter .constants {
color: #0066cc !important;
}
.syntaxhighlighter .script {
font-weight: bold !important;
color: #006699 !important;
background-color: none !important;
}
.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
color: gray !important;
}
.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
color: #ff1493 !important;
}
.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
color: red !important;
}
.syntaxhighlighter .keyword {
font-weight: bold !important;
}

View File

@ -0,0 +1,117 @@
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
.syntaxhighlighter {
background-color: white !important;
}
.syntaxhighlighter .line.alt1 {
background-color: white !important;
}
.syntaxhighlighter .line.alt2 {
background-color: white !important;
}
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
background-color: #e0e0e0 !important;
}
.syntaxhighlighter .line.highlighted.number {
color: black !important;
}
.syntaxhighlighter table caption {
color: black !important;
}
.syntaxhighlighter .gutter {
color: #afafaf !important;
}
.syntaxhighlighter .gutter .line {
border-right: 3px solid #6ce26c !important;
}
.syntaxhighlighter .gutter .line.highlighted {
background-color: #6ce26c !important;
color: white !important;
}
.syntaxhighlighter.printing .line .content {
border: none !important;
}
.syntaxhighlighter.collapsed {
overflow: visible !important;
}
.syntaxhighlighter.collapsed .toolbar {
color: blue !important;
background: white !important;
border: 1px solid #6ce26c !important;
}
.syntaxhighlighter.collapsed .toolbar a {
color: blue !important;
}
.syntaxhighlighter.collapsed .toolbar a:hover {
color: red !important;
}
.syntaxhighlighter .toolbar {
color: white !important;
background: #6ce26c !important;
border: none !important;
}
.syntaxhighlighter .toolbar a {
color: white !important;
}
.syntaxhighlighter .toolbar a:hover {
color: black !important;
}
.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
color: black !important;
}
.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
color: #008200 !important;
}
.syntaxhighlighter .string, .syntaxhighlighter .string a {
color: blue !important;
}
.syntaxhighlighter .keyword {
color: #006699 !important;
}
.syntaxhighlighter .preprocessor {
color: gray !important;
}
.syntaxhighlighter .variable {
color: #aa7700 !important;
}
.syntaxhighlighter .value {
color: #009900 !important;
}
.syntaxhighlighter .functions {
color: #ff1493 !important;
}
.syntaxhighlighter .constants {
color: #0066cc !important;
}
.syntaxhighlighter .script {
font-weight: bold !important;
color: #006699 !important;
background-color: none !important;
}
.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
color: gray !important;
}
.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
color: #ff1493 !important;
}
.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
color: red !important;
}
.syntaxhighlighter .keyword {
font-weight: bold !important;
}

View File

@ -75,7 +75,7 @@ public class TesterTest {
@Test
public void testTester() throws Exception {
if (true) return;
// if (true) return;
myRestfulServer.setProviders(new SearchProvider(), new GlobalHistoryProvider());
myServer.start();