Improve documentation on exception handling for servers

This commit is contained in:
jamesagnew 2014-08-20 22:46:15 -04:00
parent 45253cba5d
commit 731d369be0
20 changed files with 394 additions and 63 deletions

View File

@ -0,0 +1,100 @@
package example;
import java.io.IOException;
import java.lang.annotation.Documented;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.TagListParam;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.AddTags;
import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.DeleteTags;
import ca.uhn.fhir.rest.annotation.GetTags;
import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.annotation.Transaction;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
@SuppressWarnings("unused")
public abstract class ServerExceptionsExample implements IResourceProvider {
private boolean databaseIsDown;
//START SNIPPET: returnOO
@Read
public Patient read(@IdParam IdDt theId) {
if (databaseIsDown) {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setSeverity(IssueSeverityEnum.FATAL).setDetails("Database is down");
throw new InternalErrorException("Database is down", oo);
}
Patient patient = new Patient(); // populate this
return patient;
}
//END SNIPPET: returnOO
}

View File

@ -308,56 +308,6 @@
<escapeHTML>false</escapeHTML>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.16</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
</configuration>
<reportSets>
<reportSet>
<id>default</id>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>2.5.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.4</version>
<reportSets>
<reportSet>
<id>normal</id>
<reports>
<report>jxr</report>
</reports>
</reportSet>
<reportSet>
<id>restful-server-example</id>
<reports>
<report>jxr</report>
</reports>
<configuration>
<sourcePath>../restful-server-example/src/main/java</sourcePath>
<destDir>${project.reporting.outputDirectory}/rse-xref</destDir>
<outputDirectory>tmp</outputDirectory>
<reportOutputDirectory>rse-xref</reportOutputDirectory>
</configuration>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
@ -600,6 +550,74 @@
</build>
<profiles>
<profile>
<id>MINI</id>
</profile>
<profile>
<id>DEFAULT</id>
<!--
The default profile has a bunch of the site plugins that
take longer to execute, so that they can be skipped for
testing the site build
-->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.16</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
</configuration>
<reportSets>
<reportSet>
<id>default</id>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>2.5.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.4</version>
<reportSets>
<reportSet>
<id>normal</id>
<reports>
<report>jxr</report>
</reports>
</reportSet>
<reportSet>
<id>restful-server-example</id>
<reports>
<report>jxr</report>
</reports>
<configuration>
<sourcePath>../restful-server-example/src/main/java</sourcePath>
<destDir>${project.reporting.outputDirectory}/rse-xref</destDir>
<outputDirectory>tmp</outputDirectory>
<reportOutputDirectory>rse-xref</reportOutputDirectory>
</configuration>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
</profile>
<profile>
<id>DIST</id>
<build>

View File

@ -94,6 +94,9 @@
of the name "reference" for the actual reference. Thanks to
Ricky Nguyen for reporting and tracking down the issue!
</action>
<action type="fix">
Rename NotImpementedException to NotImplementedException (to correct typo)
</action>
</release>
<release version="0.5" date="2014-Jul-30">
<action type="add">

View File

@ -46,13 +46,14 @@ public abstract class BaseServerResponseException extends RuntimeException {
registerExceptionType(ResourceVersionConflictException.STATUS_CODE, ResourceVersionConflictException.class);
registerExceptionType(UnprocessableEntityException.STATUS_CODE, UnprocessableEntityException.class);
registerExceptionType(ResourceGoneException.STATUS_CODE, ResourceGoneException.class);
registerExceptionType(NotImplementedOperationException.STATUS_CODE, NotImplementedOperationException.class);
}
private final OperationOutcome myOperationOutcome;
private OperationOutcome myOperationOutcome;
private String myResponseBody;
private String myResponseMimeType;
private int myStatusCode;
/**
* Constructor
*
@ -66,7 +67,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
myStatusCode = theStatusCode;
myOperationOutcome = null;
}
/**
* Constructor
*
@ -186,6 +187,17 @@ public abstract class BaseServerResponseException extends RuntimeException {
return myStatusCode;
}
/**
* Sets the OperationOutcome resource associated with this exception. In server
* implementations, this is the OperartionOutcome resource to include with the HTTP response. In
* client implementations you should not call this method.
*
* @param theOperationOutcome The OperationOutcome resource
*/
public void setOperationOutcome(OperationOutcome theOperationOutcome) {
myOperationOutcome = theOperationOutcome;
}
/**
* This method is currently only called internally by HAPI, it should not be called by user code.
*/
@ -200,6 +212,13 @@ public abstract class BaseServerResponseException extends RuntimeException {
myResponseMimeType = theResponseMimeType;
}
/**
* For unit tests only
*/
static boolean isExceptionTypeRegistered(Class<?> theType) {
return ourStatusCodeToExceptionType.values().contains(theType);
}
public static BaseServerResponseException newInstance(int theStatusCode, String theMessage) {
if (ourStatusCodeToExceptionType.containsKey(theStatusCode)) {
try {

View File

@ -53,7 +53,7 @@ public class InternalErrorException extends BaseServerResponseException {
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public InternalErrorException(String theMessage, OperationOutcome theOperationOutcome) {
super(theMessage, theOperationOutcome);
super(STATUS_CODE, theMessage, theOperationOutcome);
}
public InternalErrorException(String theMessage) {

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.rest.server.exceptions;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/*
@ -43,5 +44,17 @@ public class InvalidRequestException extends BaseServerResponseException {
public InvalidRequestException(String theMessage) {
super(STATUS_CODE, theMessage);
}
/**
* Constructor
*
* @param theMessage
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public InvalidRequestException(String theMessage, OperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
}

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.rest.server.exceptions;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/*
@ -36,6 +37,17 @@ public class MethodNotAllowedException extends BaseServerResponseException {
public static final int STATUS_CODE = Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED;
private static final long serialVersionUID = 1L;
/**
* Constructor
*
* @param theMessage
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public MethodNotAllowedException(String theMessage, OperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
public MethodNotAllowedException(String error) {
super(STATUS_CODE, error);
}

View File

@ -1,4 +1,5 @@
package ca.uhn.fhir.rest.server.exceptions;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.rest.server.Constants;
/*
@ -37,12 +38,24 @@ import ca.uhn.fhir.rest.server.Constants;
*
*
*/
public class NotImpementedOperationException extends BaseServerResponseException {
public class NotImplementedOperationException extends BaseServerResponseException {
public static final int STATUS_CODE = Constants.STATUS_HTTP_501_NOT_IMPLEMENTED;
private static final long serialVersionUID = 1L;
public NotImpementedOperationException(String theMessage) {
public NotImplementedOperationException(String theMessage) {
super(STATUS_CODE, theMessage);
}
/**
* Constructor
*
* @param theMessage
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public NotImplementedOperationException(String theMessage, OperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
}

View File

@ -22,11 +22,12 @@ 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.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.Constants;
/**
* Represents an <b>HTTP 410 Resource Gone</b> response, which gvenerally
* Represents an <b>HTTP 410 Resource Gone</b> response, which geenerally
* indicates that the resource has been deleted
*/
public class ResourceGoneException extends BaseServerResponseException {
@ -45,6 +46,17 @@ public class ResourceGoneException extends BaseServerResponseException {
super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + thePatientId + " is gone/deleted");
}
/**
* Constructor
*
* @param theMessage
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public ResourceGoneException(String theMessage, OperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
public ResourceGoneException(String theMessage) {
super(STATUS_CODE, theMessage);
}

View File

@ -43,6 +43,17 @@ public class ResourceNotFoundException extends BaseServerResponseException {
super(STATUS_CODE, createErrorMessage(theClass, theId), theOperationOutcome);
}
/**
* Constructor
*
* @param theMessage
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public ResourceNotFoundException(String theMessage, OperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
/**
* @deprecated This doesn't make sense, since an identifier is not a resource ID and shouldn't generate a 404 if it isn't found - Should be removed
*/

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.server.exceptions;
* #L%
*/
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.server.Constants;
@ -36,4 +37,16 @@ public class ResourceVersionConflictException extends BaseServerResponseExceptio
public ResourceVersionConflictException(String error) {
super(STATUS_CODE, error);
}
/**
* Constructor
*
* @param theMessage
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public ResourceVersionConflictException(String theMessage, OperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
}

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.server.exceptions;
* #L%
*/
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.server.Constants;
@ -35,4 +36,16 @@ public class ResourceVersionNotSpecifiedException extends BaseServerResponseExce
public ResourceVersionNotSpecifiedException(String error) {
super(STATUS_CODE, error);
}
/**
* Constructor
*
* @param theMessage
* The message
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public ResourceVersionNotSpecifiedException(String theMessage, OperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server.exceptions;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
/*
* #%L
* HAPI FHIR - Core Library
@ -38,6 +40,19 @@ public class UnclassifiedServerFailureException extends BaseServerResponseExcept
super(theStatusCode, theMessage);
}
/**
* Constructor
*
* @param theStatusCode
* The HTTP status code to return (e.g. 404 if you wish to return an HTTP 404 status)
* @param theMessage
* The message to add to the status line
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public UnclassifiedServerFailureException(int theStatusCode, String theMessage, OperationOutcome theOperationOutcome) {
super(theStatusCode, theMessage, theOperationOutcome);
}
private static final long serialVersionUID = 1L;
}

View File

@ -38,6 +38,18 @@ public class UnprocessableEntityException extends BaseServerResponseException {
private static final long serialVersionUID = 1L;
public static final int STATUS_CODE = Constants.STATUS_HTTP_422_UNPROCESSABLE_ENTITY;
/**
* Constructor
*
* @param theMessage
* The message to add to the status line
* @param theOperationOutcome The OperationOutcome resource to return to the client
*/
public UnprocessableEntityException(String theMessage, OperationOutcome theOperationOutcome) {
super(STATUS_CODE, theMessage, theOperationOutcome);
}
/**
* Constructor which accepts an {@link OperationOutcome} resource which will be supplied in the response
*/

View File

@ -127,12 +127,16 @@ public class FhirTerser {
* message. Specifying a type of {@link IResource} would return the resource itself, as well as any contained
* resources.
* </p>
* <p>
* Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not
* decend into linked resources (e.g. {@link ResourceReferenceDt#getResource()})
* </p>
*
* @param theResourceT
* @param theResource
* The resource instance to search. Must not be null.
* @param theType
* The type to search for. Must not be null.
* @return
* @return Returns a list of all matching elements
*/
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(IResource theResource, final Class<T> theType) {
final ArrayList<T> retVal = new ArrayList<T>();

View File

@ -416,6 +416,10 @@
case, you may pass one into the constructor of the
exception you are throwing.
</p>
<macro name="snippet">
<param name="id" value="returnOO" />
<param name="file" value="examples/src/main/java/example/ServerExceptionsExample.java" />
</macro>
</subsection>
</section>

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.rest.server.exception;
package ca.uhn.fhir.rest.server.exceptions;
import static org.junit.Assert.*;
@ -10,6 +10,7 @@ import org.mockito.internal.matchers.GreaterThan;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
@ -29,12 +30,21 @@ public class ExceptionTest {
Class<?> next = Class.forName(classInfo.getName());
assertNotNull(next);
if (next == AuthenticationException.class) {
if (next == getClass()) {
continue;
}
if (next == BaseServerResponseException.class) {
continue;
}
if (next == UnclassifiedServerFailureException.class) {
continue;
}
assertTrue(BaseServerResponseException.isExceptionTypeRegistered(next));
if (next == AuthenticationException.class) {
continue;
}
try {
next.getConstructor(String.class, OperationOutcome.class);

View File

@ -0,0 +1,59 @@
package ca.uhn.fhir.util;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Observation;
public class FhirTerserTest {
@Test
public void testTerser() {
//@formatter:off
String msg = "<Observation xmlns=\"http://hl7.org/fhir\">\n" +
" <text>\n" +
" <status value=\"empty\"/>\n" +
" <div xmlns=\"http://www.w3.org/1999/xhtml\"/>\n" +
" </text>\n" +
" <!-- The test code - may not be correct -->\n" +
" <name>\n" +
" <coding>\n" +
" <system value=\"http://loinc.org\"/>\n" +
" <code value=\"43151-0\"/>\n" +
" <display value=\"Glucose Meter Device Panel\"/>\n" +
" </coding>\n" +
" </name>\n" +
" <valueQuantity>\n" +
" <value value=\"7.7\"/>\n" +
" <units value=\"mmol/L\"/>\n" +
" <system value=\"http://unitsofmeasure.org\"/>\n" +
" </valueQuantity>\n" +
" <appliesDateTime value=\"2014-05-28T22:12:21Z\"/>\n" +
" <status value=\"final\"/>\n" +
" <reliability value=\"ok\"/>\n" +
" <subject>\n" +
" <reference value=\"cid:patient@bundle\"/>\n" +
" </subject>\n" +
" <performer>\n" +
" <reference value=\"cid:device@bundle\"></reference>\n" +
" </performer>\n" +
"</Observation>";
//@formatter:on
Observation parsed = ourCtx.newXmlParser().parseResource(Observation.class, msg);
FhirTerser t = ourCtx.newTerser();
List<ResourceReferenceDt> elems = t.getAllPopulatedChildElementsOfType(parsed, ResourceReferenceDt.class);
assertEquals(2, elems.size());
assertEquals("cid:patient@bundle", elems.get(0).getReference().getValue());
assertEquals("cid:device@bundle", elems.get(1).getReference().getValue());
}
private static FhirContext ourCtx = new FhirContext();
}

View File

@ -12,7 +12,7 @@
<dependent-module archiveName="hapi-fhir-base-0.6-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
<dependency-type>uses</dependency-type>
</dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-testpage-overlay?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">
<dependent-module deploy-path="/" handle="module:/overlay/var/M2_REPO/ca/uhn/hapi/fhir/hapi-fhir-testpage-overlay/0.6-SNAPSHOT/hapi-fhir-testpage-overlay-0.6-SNAPSHOT.war?unpackFolder=target/m2e-wtp/overlays&amp;includes=**/**&amp;excludes=META-INF/MANIFEST.MF">
<dependency-type>consumes</dependency-type>
</dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">

View File

@ -6,7 +6,7 @@
<dependent-module archiveName="hapi-fhir-base-0.6-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
<dependency-type>uses</dependency-type>
</dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-testpage-overlay?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">
<dependent-module deploy-path="/" handle="module:/overlay/var/M2_REPO/ca/uhn/hapi/fhir/hapi-fhir-testpage-overlay/0.6-SNAPSHOT/hapi-fhir-testpage-overlay-0.6-SNAPSHOT.war?unpackFolder=target/m2e-wtp/overlays&amp;includes=**/**&amp;excludes=META-INF/MANIFEST.MF">
<dependency-type>consumes</dependency-type>
</dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">