> 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()+">");
+ }
+ }
+
+}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
index c3ffd9cf8ef..154dac73b2f 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
@@ -89,12 +89,10 @@ public class RestfulServer extends HttpServlet {
}
/**
- * This method is called prior to sending a response to incoming requests.
- * It is used to add custom headers.
+ * This method is called prior to sending a response to incoming requests. It is used to add custom headers.
*
- * Use caution if overriding this method: it is recommended to call
- * super.addHeadersToResponse
to avoid inadvertantly disabling
- * functionality.
+ * Use caution if overriding this method: it is recommended to call super.addHeadersToResponse
to avoid
+ * inadvertantly disabling functionality.
*
*/
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
@@ -102,17 +100,15 @@ public class RestfulServer extends HttpServlet {
}
/**
- * Gets the {@link FhirContext} associated with this server. For efficient
- * processing, resource providers and plain providers should generally use
- * this context if one is needed, as opposed to creating their own.
+ * Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain
+ * providers should generally use this context if one is needed, as opposed to creating their own.
*/
public FhirContext getFhirContext() {
return myFhirContext;
}
/**
- * Provides the non-resource specific providers which implement method calls
- * on this server
+ * Provides the non-resource specific providers which implement method calls on this server
*
* @see #getResourceProviders()
*/
@@ -139,12 +135,11 @@ public class RestfulServer extends HttpServlet {
}
/**
- * Returns the server conformance provider, which is the provider that is
- * used to generate the server's conformance (metadata) statement.
+ * Returns the server conformance provider, which is the provider that is used to generate the server's conformance
+ * (metadata) statement.
*
- * By default, the {@link ServerConformanceProvider} is used, but this can
- * be changed, or set to null
if you do not wish to export a
- * conformance statement.
+ * By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to null
+ * if you do not wish to export a conformance statement.
*
*/
public Object getServerConformanceProvider() {
@@ -152,9 +147,8 @@ public class RestfulServer extends HttpServlet {
}
/**
- * Gets the server's name, as exported in conformance profiles exported by
- * the server. This is informational only, but can be helpful to set with
- * something appropriate.
+ * Gets the server's name, as exported in conformance profiles exported by the server. This is informational only,
+ * but can be helpful to set with something appropriate.
*
* @see RestfulServer#setServerName(StringDt)
*/
@@ -167,19 +161,17 @@ public class RestfulServer extends HttpServlet {
}
/**
- * Gets the server's version, as exported in conformance profiles exported
- * by the server. This is informational only, but can be helpful to set with
- * something appropriate.
+ * Gets the server's version, as exported in conformance profiles exported by the server. This is informational
+ * only, but can be helpful to set with something appropriate.
*/
public String getServerVersion() {
return myServerVersion;
}
/**
- * Initializes the server. Note that this method is final to avoid
- * accidentally introducing bugs in implementations, but subclasses may put
- * initialization code in {@link #initialize()}, which is called immediately
- * before beginning initialization of the restful server's internal init.
+ * Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations,
+ * but subclasses may put initialization code in {@link #initialize()}, which is called immediately before beginning
+ * initialization of the restful server's internal init.
*/
@Override
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
- * this server.
+ * Sets the non-resource specific providers which implement method calls on this server.
*
* @see #setResourceProviders(Collection)
*/
@@ -247,8 +238,7 @@ public class RestfulServer extends HttpServlet {
}
/**
- * Sets the non-resource specific providers which implement method calls on
- * this server.
+ * Sets the non-resource specific providers which implement method calls on this server.
*
* @see #setResourceProviders(Collection)
*/
@@ -257,8 +247,7 @@ public class RestfulServer extends HttpServlet {
}
/**
- * Sets the non-resource specific providers which implement method calls on
- * this server
+ * Sets the non-resource specific providers which implement method calls on this server
*
* @see #setResourceProviders(Collection)
*/
@@ -288,19 +277,16 @@ public class RestfulServer extends HttpServlet {
}
/**
- * Returns the server conformance provider, which is the provider that is
- * used to generate the server's conformance (metadata) statement.
+ * Returns the server conformance provider, which is the provider that is used to generate the server's conformance
+ * (metadata) statement.
*
- * By default, the {@link ServerConformanceProvider} is used, but this can
- * be changed, or set to null
if you do not wish to export a
- * conformance statement.
+ * By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to null
+ * if you do not wish to export a conformance statement.
*
- * Note that this method can only be called before the server is
- * initialized.
+ * Note that this method can only be called before the server is initialized.
*
* @throws IllegalStateException
- * Note that this method can only be called prior to
- * {@link #init() initialization} and will throw an
+ * Note that this method can only be called prior to {@link #init() initialization} and will throw an
* {@link IllegalStateException} if called after that.
*/
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
- * the server. This is informational only, but can be helpful to set with
- * something appropriate.
+ * Gets the server's name, as exported in conformance profiles exported by the server. This is informational only,
+ * but can be helpful to set with something appropriate.
*
* @see RestfulServer#setServerName(StringDt)
*/
@@ -322,18 +307,16 @@ public class RestfulServer extends HttpServlet {
}
/**
- * Gets the server's version, as exported in conformance profiles exported
- * by the server. This is informational only, but can be helpful to set with
- * something appropriate.
+ * Gets the server's version, as exported in conformance profiles exported by the server. This is informational
+ * only, but can be helpful to set with something appropriate.
*/
public void setServerVersion(String theServerVersion) {
myServerVersion = theServerVersion;
}
/**
- * If set to true
(default is false), the server will use
- * browser friendly content-types (instead of standard FHIR ones) when it
- * detects that the request is coming from a browser instead of a FHIR
+ * If set to true
(default is false), the server will use browser friendly content-types (instead of
+ * standard FHIR ones) when it detects that the request is coming from a browser instead of a FHIR
*/
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
@@ -361,7 +344,7 @@ public class RestfulServer extends HttpServlet {
private void findResourceMethods(Object theProvider, Class> clazz) {
for (Method m : clazz.getDeclaredMethods()) {
if (!Modifier.isPublic(m.getModifiers())) {
- ourLog.debug("Ignoring non-public method: {}",m);
+ ourLog.debug("Ignoring non-public method: {}", m);
} else {
if (!Modifier.isStatic(m.getModifiers())) {
ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
@@ -521,13 +504,12 @@ public class RestfulServer extends HttpServlet {
Map params = new HashMap(theRequest.getParameterMap());
StringTokenizer tok = new StringTokenizer(requestPath, "/");
- if (!tok.hasMoreTokens()) {
- throw new ResourceNotFoundException("No resource name specified");
- }
- resourceName = tok.nextToken();
- if (resourceName.startsWith("_")) {
- operation = resourceName;
- resourceName = null;
+ if (tok.hasMoreTokens()) {
+ resourceName = tok.nextToken();
+ if (resourceName.startsWith("_")) {
+ operation = resourceName;
+ resourceName = null;
+ }
}
ResourceBinding resourceBinding = null;
@@ -570,16 +552,16 @@ public class RestfulServer extends HttpServlet {
}
// Secondary is for things like ..../_tags/_delete
- String secondaryOperation=null;
-
+ String secondaryOperation = null;
+
while (tok.hasMoreTokens()) {
String nextString = tok.nextToken();
if (operation == null) {
operation = nextString;
- }else if (secondaryOperation==null) {
- secondaryOperation=nextString;
- }else {
- throw new InvalidRequestException("URL path has unexpected token '"+nextString + "' at the end: " + requestPath);
+ } else if (secondaryOperation == null) {
+ secondaryOperation = nextString;
+ } else {
+ 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
- * that needs to be performed prior to the server being used.
+ * This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the
+ * server being used.
*/
protected void initialize() {
// nothing by default
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TransactionTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TransactionTest.java
new file mode 100644
index 00000000000..96a24c741a8
--- /dev/null
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TransactionTest.java
@@ -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 transaction(@TransactionParam List theResources) {
+ return theResources;
+ }
+
+
+ }
+
+}