More work on transaction method
This commit is contained in:
parent
62b909ff22
commit
30065fdfae
|
@ -0,0 +1,41 @@
|
||||||
|
package ca.uhn.fhir.rest.annotation;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RESTful method annotation to be used for the FHIR <a href="http://hl7.org/implement/standards/fhir/http.html#transaction">transaction</a> method.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method should have a parameter of type {@link Bundle} annotated with the {@link TransactionParam} annotation.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
public @interface Transaction {
|
||||||
|
// nothing
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package ca.uhn.fhir.rest.annotation;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(value=ElementType.PARAMETER)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface TransactionParam {
|
||||||
|
|
||||||
|
}
|
|
@ -56,6 +56,7 @@ import ca.uhn.fhir.rest.annotation.History;
|
||||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||||
import ca.uhn.fhir.rest.annotation.Read;
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Transaction;
|
||||||
import ca.uhn.fhir.rest.annotation.Update;
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
import ca.uhn.fhir.rest.annotation.Validate;
|
import ca.uhn.fhir.rest.annotation.Validate;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
@ -172,8 +173,9 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||||
GetTags getTags = theMethod.getAnnotation(GetTags.class);
|
GetTags getTags = theMethod.getAnnotation(GetTags.class);
|
||||||
AddTags addTags = theMethod.getAnnotation(AddTags.class);
|
AddTags addTags = theMethod.getAnnotation(AddTags.class);
|
||||||
DeleteTags deleteTags = theMethod.getAnnotation(DeleteTags.class);
|
DeleteTags deleteTags = theMethod.getAnnotation(DeleteTags.class);
|
||||||
|
Transaction transaction = theMethod.getAnnotation(Transaction.class);
|
||||||
// ** if you add another annotation above, also add it to the next line:
|
// ** if you add another annotation above, also add it to the next line:
|
||||||
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags)) {
|
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags,transaction)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,6 +285,8 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||||
return new AddTagsMethodBinding(theMethod, theContext, theProvider, addTags);
|
return new AddTagsMethodBinding(theMethod, theContext, theProvider, addTags);
|
||||||
} else if (deleteTags != null) {
|
} else if (deleteTags != null) {
|
||||||
return new DeleteTagsMethodBinding(theMethod, theContext, theProvider, deleteTags);
|
return new DeleteTagsMethodBinding(theMethod, theContext, theProvider, deleteTags);
|
||||||
|
} else if (transaction != null) {
|
||||||
|
return new TransactionMethodBinding(theMethod, theContext, theProvider);
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,13 +209,15 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
||||||
if (uaHeader != null && uaHeader.contains("Mozilla")) {
|
if (uaHeader != null && uaHeader.contains("Mozilla")) {
|
||||||
requestIsBrowser = true;
|
requestIsBrowser = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object requestObject = parseRequestObject(theRequest);
|
||||||
|
|
||||||
// Method params
|
// Method params
|
||||||
Object[] params = new Object[getParameters().size()];
|
Object[] params = new Object[getParameters().size()];
|
||||||
for (int i = 0; i < getParameters().size(); i++) {
|
for (int i = 0; i < getParameters().size(); i++) {
|
||||||
IParameter param = getParameters().get(i);
|
IParameter param = getParameters().get(i);
|
||||||
if (param != null) {
|
if (param != null) {
|
||||||
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, null);
|
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, requestObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,6 +237,13 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses may override
|
||||||
|
*/
|
||||||
|
protected Object parseRequestObject(@SuppressWarnings("unused") Request theRequest) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
|
protected static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
|
||||||
Object retValObj = theResourceMetadata.get(theKey);
|
Object retValObj = theResourceMetadata.get(theKey);
|
||||||
if (retValObj == null) {
|
if (retValObj == null) {
|
||||||
|
|
|
@ -1,45 +1,45 @@
|
||||||
package ca.uhn.fhir.rest.method;
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
import java.io.IOException;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
import java.io.Reader;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||||
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.param.TransactionParameter;
|
||||||
|
import ca.uhn.fhir.rest.param.IParameter;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
public class TransactionMethodBinding extends BaseResourceReturningMethodBinding {
|
public class TransactionMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
|
|
||||||
|
private int myResourceParameterIndex;
|
||||||
|
|
||||||
public TransactionMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
|
public TransactionMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
|
||||||
super(null, theMethod, theConetxt, theProvider);
|
super(null, theMethod, theConetxt, theProvider);
|
||||||
}
|
|
||||||
|
myResourceParameterIndex = -1;
|
||||||
|
int index=0;
|
||||||
|
for (IParameter next : getParameters()) {
|
||||||
|
if (next instanceof TransactionParameter) {
|
||||||
|
myResourceParameterIndex = index;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
if (myResourceParameterIndex==-1) {
|
||||||
public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " does not have a parameter annotated with the @" + TransactionParam.class + " annotation");
|
||||||
// TODO Auto-generated method stub
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getResourceName() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RestfulOperationTypeEnum getResourceOperationType() {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -47,22 +47,19 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
||||||
return RestfulOperationSystemEnum.TRANSACTION;
|
return RestfulOperationSystemEnum.TRANSACTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||||
// TODO Auto-generated method stub
|
if (theRequest.getRequestType() != RequestType.POST) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
if (isNotBlank(theRequest.getOperation())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isNotBlank(theRequest.getResourceName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -72,6 +69,26 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
public List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<IResource> retVal=(List<IResource>) invokeServerMethod(theMethodParams);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object parseRequestObject(Request theRequest) {
|
||||||
|
EncodingEnum encoding = determineResponseEncoding(theRequest);
|
||||||
|
IParser parser = encoding.newParser(getContext());
|
||||||
|
Bundle bundle = parser.parseBundle(theRequest.getInputReader());
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestfulOperationTypeEnum getResourceOperationType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
import ca.uhn.fhir.model.api.annotation.TagListParam;
|
import ca.uhn.fhir.model.api.annotation.TagListParam;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.model.primitive.IntegerDt;
|
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Count;
|
import ca.uhn.fhir.rest.annotation.Count;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
||||||
|
@ -225,6 +226,8 @@ public class ParameterUtil {
|
||||||
param = new CountParameter();
|
param = new CountParameter();
|
||||||
} else if (nextAnnotation instanceof Sort) {
|
} else if (nextAnnotation instanceof Sort) {
|
||||||
param = new SortParameter();
|
param = new SortParameter();
|
||||||
|
} else if (nextAnnotation instanceof TransactionParam) {
|
||||||
|
param = new TransactionParameter();
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package ca.uhn.fhir.rest.param;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||||
|
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||||
|
import ca.uhn.fhir.rest.method.Request;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
|
public class TransactionParameter implements IParameter {
|
||||||
|
|
||||||
|
public TransactionParameter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
|
||||||
|
Bundle resource = (Bundle) theRequestContents;
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
||||||
|
if (theOuterCollectionType != null) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + TransactionParam.class.getName() + " but can not be a collection of collections");
|
||||||
|
}
|
||||||
|
if (theInnerCollectionType.equals(List.class)==false) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + TransactionParam.class.getName() + " but is not of type List<" + IResource.class.getCanonicalName()+">");
|
||||||
|
}
|
||||||
|
if (theParameterType.equals(IResource.class)==false) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + TransactionParam.class.getName() + " but is not of type List<" + IResource.class.getCanonicalName()+">");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -89,12 +89,10 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called prior to sending a response to incoming requests.
|
* This method is called prior to sending a response to incoming requests. It is used to add custom headers.
|
||||||
* It is used to add custom headers.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Use caution if overriding this method: it is recommended to call
|
* Use caution if overriding this method: it is recommended to call <code>super.addHeadersToResponse</code> to avoid
|
||||||
* <code>super.addHeadersToResponse</code> to avoid inadvertantly disabling
|
* inadvertantly disabling functionality.
|
||||||
* functionality.
|
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
||||||
|
@ -102,17 +100,15 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link FhirContext} associated with this server. For efficient
|
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain
|
||||||
* processing, resource providers and plain providers should generally use
|
* providers should generally use this context if one is needed, as opposed to creating their own.
|
||||||
* this context if one is needed, as opposed to creating their own.
|
|
||||||
*/
|
*/
|
||||||
public FhirContext getFhirContext() {
|
public FhirContext getFhirContext() {
|
||||||
return myFhirContext;
|
return myFhirContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the non-resource specific providers which implement method calls
|
* Provides the non-resource specific providers which implement method calls on this server
|
||||||
* on this server
|
|
||||||
*
|
*
|
||||||
* @see #getResourceProviders()
|
* @see #getResourceProviders()
|
||||||
*/
|
*/
|
||||||
|
@ -139,12 +135,11 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the server conformance provider, which is the provider that is
|
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||||
* used to generate the server's conformance (metadata) statement.
|
* (metadata) statement.
|
||||||
* <p>
|
* <p>
|
||||||
* By default, the {@link ServerConformanceProvider} is used, but this can
|
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code>
|
||||||
* be changed, or set to <code>null</code> if you do not wish to export a
|
* if you do not wish to export a conformance statement.
|
||||||
* conformance statement.
|
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public Object getServerConformanceProvider() {
|
public Object getServerConformanceProvider() {
|
||||||
|
@ -152,9 +147,8 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the server's name, as exported in conformance profiles exported by
|
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||||
* the server. This is informational only, but can be helpful to set with
|
* but can be helpful to set with something appropriate.
|
||||||
* something appropriate.
|
|
||||||
*
|
*
|
||||||
* @see RestfulServer#setServerName(StringDt)
|
* @see RestfulServer#setServerName(StringDt)
|
||||||
*/
|
*/
|
||||||
|
@ -167,19 +161,17 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the server's version, as exported in conformance profiles exported
|
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational
|
||||||
* by the server. This is informational only, but can be helpful to set with
|
* only, but can be helpful to set with something appropriate.
|
||||||
* something appropriate.
|
|
||||||
*/
|
*/
|
||||||
public String getServerVersion() {
|
public String getServerVersion() {
|
||||||
return myServerVersion;
|
return myServerVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the server. Note that this method is final to avoid
|
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations,
|
||||||
* accidentally introducing bugs in implementations, but subclasses may put
|
* but subclasses may put initialization code in {@link #initialize()}, which is called immediately before beginning
|
||||||
* initialization code in {@link #initialize()}, which is called immediately
|
* initialization of the restful server's internal init.
|
||||||
* before beginning initialization of the restful server's internal init.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final void init() throws ServletException {
|
public final void init() throws ServletException {
|
||||||
|
@ -237,8 +229,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the non-resource specific providers which implement method calls on
|
* Sets the non-resource specific providers which implement method calls on this server.
|
||||||
* this server.
|
|
||||||
*
|
*
|
||||||
* @see #setResourceProviders(Collection)
|
* @see #setResourceProviders(Collection)
|
||||||
*/
|
*/
|
||||||
|
@ -247,8 +238,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the non-resource specific providers which implement method calls on
|
* Sets the non-resource specific providers which implement method calls on this server.
|
||||||
* this server.
|
|
||||||
*
|
*
|
||||||
* @see #setResourceProviders(Collection)
|
* @see #setResourceProviders(Collection)
|
||||||
*/
|
*/
|
||||||
|
@ -257,8 +247,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the non-resource specific providers which implement method calls on
|
* Sets the non-resource specific providers which implement method calls on this server
|
||||||
* this server
|
|
||||||
*
|
*
|
||||||
* @see #setResourceProviders(Collection)
|
* @see #setResourceProviders(Collection)
|
||||||
*/
|
*/
|
||||||
|
@ -288,19 +277,16 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the server conformance provider, which is the provider that is
|
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||||
* used to generate the server's conformance (metadata) statement.
|
* (metadata) statement.
|
||||||
* <p>
|
* <p>
|
||||||
* By default, the {@link ServerConformanceProvider} is used, but this can
|
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code>
|
||||||
* be changed, or set to <code>null</code> if you do not wish to export a
|
* if you do not wish to export a conformance statement.
|
||||||
* conformance statement.
|
|
||||||
* </p>
|
* </p>
|
||||||
* Note that this method can only be called before the server is
|
* Note that this method can only be called before the server is initialized.
|
||||||
* initialized.
|
|
||||||
*
|
*
|
||||||
* @throws IllegalStateException
|
* @throws IllegalStateException
|
||||||
* Note that this method can only be called prior to
|
* Note that this method can only be called prior to {@link #init() initialization} and will throw an
|
||||||
* {@link #init() initialization} and will throw an
|
|
||||||
* {@link IllegalStateException} if called after that.
|
* {@link IllegalStateException} if called after that.
|
||||||
*/
|
*/
|
||||||
public void setServerConformanceProvider(Object theServerConformanceProvider) {
|
public void setServerConformanceProvider(Object theServerConformanceProvider) {
|
||||||
|
@ -311,9 +297,8 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the server's name, as exported in conformance profiles exported by
|
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||||
* the server. This is informational only, but can be helpful to set with
|
* but can be helpful to set with something appropriate.
|
||||||
* something appropriate.
|
|
||||||
*
|
*
|
||||||
* @see RestfulServer#setServerName(StringDt)
|
* @see RestfulServer#setServerName(StringDt)
|
||||||
*/
|
*/
|
||||||
|
@ -322,18 +307,16 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the server's version, as exported in conformance profiles exported
|
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational
|
||||||
* by the server. This is informational only, but can be helpful to set with
|
* only, but can be helpful to set with something appropriate.
|
||||||
* something appropriate.
|
|
||||||
*/
|
*/
|
||||||
public void setServerVersion(String theServerVersion) {
|
public void setServerVersion(String theServerVersion) {
|
||||||
myServerVersion = theServerVersion;
|
myServerVersion = theServerVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to <code>true</code> (default is false), the server will use
|
* If set to <code>true</code> (default is false), the server will use browser friendly content-types (instead of
|
||||||
* browser friendly content-types (instead of standard FHIR ones) when it
|
* standard FHIR ones) when it detects that the request is coming from a browser instead of a FHIR
|
||||||
* detects that the request is coming from a browser instead of a FHIR
|
|
||||||
*/
|
*/
|
||||||
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
|
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
|
||||||
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
||||||
|
@ -361,7 +344,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
private void findResourceMethods(Object theProvider, Class<?> clazz) {
|
private void findResourceMethods(Object theProvider, Class<?> clazz) {
|
||||||
for (Method m : clazz.getDeclaredMethods()) {
|
for (Method m : clazz.getDeclaredMethods()) {
|
||||||
if (!Modifier.isPublic(m.getModifiers())) {
|
if (!Modifier.isPublic(m.getModifiers())) {
|
||||||
ourLog.debug("Ignoring non-public method: {}",m);
|
ourLog.debug("Ignoring non-public method: {}", m);
|
||||||
} else {
|
} else {
|
||||||
if (!Modifier.isStatic(m.getModifiers())) {
|
if (!Modifier.isStatic(m.getModifiers())) {
|
||||||
ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
|
ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
|
||||||
|
@ -521,13 +504,12 @@ public class RestfulServer extends HttpServlet {
|
||||||
Map<String, String[]> params = new HashMap<String, String[]>(theRequest.getParameterMap());
|
Map<String, String[]> params = new HashMap<String, String[]>(theRequest.getParameterMap());
|
||||||
|
|
||||||
StringTokenizer tok = new StringTokenizer(requestPath, "/");
|
StringTokenizer tok = new StringTokenizer(requestPath, "/");
|
||||||
if (!tok.hasMoreTokens()) {
|
if (tok.hasMoreTokens()) {
|
||||||
throw new ResourceNotFoundException("No resource name specified");
|
resourceName = tok.nextToken();
|
||||||
}
|
if (resourceName.startsWith("_")) {
|
||||||
resourceName = tok.nextToken();
|
operation = resourceName;
|
||||||
if (resourceName.startsWith("_")) {
|
resourceName = null;
|
||||||
operation = resourceName;
|
}
|
||||||
resourceName = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceBinding resourceBinding = null;
|
ResourceBinding resourceBinding = null;
|
||||||
|
@ -570,16 +552,16 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secondary is for things like ..../_tags/_delete
|
// Secondary is for things like ..../_tags/_delete
|
||||||
String secondaryOperation=null;
|
String secondaryOperation = null;
|
||||||
|
|
||||||
while (tok.hasMoreTokens()) {
|
while (tok.hasMoreTokens()) {
|
||||||
String nextString = tok.nextToken();
|
String nextString = tok.nextToken();
|
||||||
if (operation == null) {
|
if (operation == null) {
|
||||||
operation = nextString;
|
operation = nextString;
|
||||||
}else if (secondaryOperation==null) {
|
} else if (secondaryOperation == null) {
|
||||||
secondaryOperation=nextString;
|
secondaryOperation = nextString;
|
||||||
}else {
|
} else {
|
||||||
throw new InvalidRequestException("URL path has unexpected token '"+nextString + "' at the end: " + requestPath);
|
throw new InvalidRequestException("URL path has unexpected token '" + nextString + "' at the end: " + requestPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -653,8 +635,8 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method may be overridden by subclasses to do perform initialization
|
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the
|
||||||
* that needs to be performed prior to the server being used.
|
* server being used.
|
||||||
*/
|
*/
|
||||||
protected void initialize() {
|
protected void initialize() {
|
||||||
// nothing by default
|
// nothing by default
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Transaction;
|
||||||
|
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||||
|
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public class TransactionTest {
|
||||||
|
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TransactionTest.class);
|
||||||
|
private static int ourPort;
|
||||||
|
private static Server ourServer;
|
||||||
|
private static FhirContext ourCtx = new FhirContext();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransaction() throws Exception {
|
||||||
|
Bundle b= new Bundle();
|
||||||
|
|
||||||
|
Patient p1 = new Patient();
|
||||||
|
p1.getId().setValue("1");
|
||||||
|
b.addEntry().setResource(p1);
|
||||||
|
|
||||||
|
Patient p2 = new Patient();
|
||||||
|
p2.getId().setValue("2");
|
||||||
|
b.addEntry().setResource(p2);
|
||||||
|
|
||||||
|
BundleEntry deletedEntry = b.addEntry();
|
||||||
|
deletedEntry.setId(new IdDt("3"));
|
||||||
|
deletedEntry.setDeleted(new InstantDt());
|
||||||
|
|
||||||
|
String bundleString = ourCtx.newXmlParser().encodeBundleToString(b);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/");
|
||||||
|
httpPost.setEntity(new StringEntity(bundleString, ContentType.create(Constants.CT_ATOM_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
Bundle bundle = new FhirContext().newXmlParser().parseBundle(responseContent);
|
||||||
|
assertEquals(3, bundle.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = RandomServerPortProvider.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
DummyProvider patientProvider = new DummyProvider();
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
servlet.setProviders(patientProvider);
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public static class DummyProvider {
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
public List<IResource> transaction(@TransactionParam List<IResource> theResources) {
|
||||||
|
return theResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue