First make sure you have a proper name and description in the POM of your RESTful service project. They will be used as part of the documentation.

To show the correct documentation at run time a JSON file with documentation metadata needs to be created when the bundle is compile. To activate this add this plugins to your POM:

<plugin>
  <groupId>org.apache.felix</groupId>
  <artifactId>maven-bundle-plugin</artifactId>
  <configuration>
    <instructions>
      <Caravan-JaxRs-ApplicationPath>/myproject/myservice</Caravan-JaxRs-ApplicationPath>
    </instructions>
  </configuration>
</plugin>

<plugin>
  <groupId>io.wcm.caravan.maven.plugins</groupId>
  <artifactId>hal-docs-maven-plugin</artifactId>
  <executions>
    <execution>
      <goals>
        <goal>generate-hal-docs-json</goal>
      </goals>
    </execution>          
  </executions>
</plugin>

The Service ID is detected automatically from the Caravan-JaxRs-ApplicationPath of the maven-bundle-plugin.

You have to create an annotated ServiceInfo class within your bundle which contains a constant for each link relation and additional documentation metadata. The name of the class is not relevant and it does not have to be exported via OSGi, it is only inspected at compile time. But the link relation constants defined in the project should be used in the implementation code of the service as well to have a single source of truth for them.

Example:

/**
 * This is a service description.
 * <p>You can use HTML tags with <strong>formatting</strong> as well here.</p>.
 */
@ServiceDoc
public final class ServiceInfo {

  private ServiceInfo() {
    // constants only
  }

  /**
   * This is the documentation of the "self" link relation.
   */
  @LinkRelationDoc
  public static final String SELF = "self";

  /**
   * If the resource contains nested HAL resources their link relations should be referenced here.
   */
  @LinkRelationDoc(links = {
      @LinkRelationRefDoc("myapi:item1"),
      @LinkRelationRefDoc(value = "myapi:item2", 
        description = "A description specific to the relation of rel1 and item2")
  })
  public static final String REL1 = "myapi:rel1";

  /**
   * To document the payload JSON of the HAL reponse either a JSON schema reference or a model 
   * class can be given. If it is a model class the JSON schema reference is detected 
   * automatically if the schema was published with the haldocs-maven-plugin as well.
   * Embedded resources can be described as well.
   */
  @LinkRelationDoc(model = Rel2Model.class, embedded = {
      @ResourceRefDoc(value = "item", model = Item.class)
  })
  public static final String REL2 = "myapi:rel2";

}

The RESTful service produces HAL resources and link that just contain the link relations, no curie links or link titles have to be added. They are added by the HAL docs implementation automatically. To make this happen a HalDocsAugmenter has to be integrated in your JAX-RS message body writer. Example:

@Component
@Service(value = JaxRsComponent.class, serviceFactory = true)
@Property(name = JaxRsComponent.PROPERTY_GLOBAL_COMPONENT, value = "true")
@Provider
@Produces(HalResource.CONTENT_TYPE)
public class HalResourceMessageBodyWriter implements MessageBodyWriter<HalResource>, JaxRsComponent {

  @Reference
  private HalDocsAugmenterFactory halDocsAugmenterFactory;

  private JsonNodeMessageBodyWriter jsonWriter = new JsonNodeMessageBodyWriter();
  private HalDocsAugmenter halDocsAugmenter;

  @Activate
  protected void activate(ComponentContext componentContext) {
    halDocsAugmenter = halDocsAugmenterFactory.create(componentContext.getUsingBundle());
  }

  @Override
  public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations,
      MediaType mediaType) {
    return (HalResource.class.isAssignableFrom(type));
  }

  @Override
  public long getSize(HalResource t, Class<?> type, Type genericType, Annotation[] annotations,
      MediaType mediaType) {
    return -1;
  }

  @Override
  public void writeTo(HalResource t, Class<?> type, Type genericType, Annotation[] annotations, 
      MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
      throws IOException, WebApplicationException {
    halDocsAugmenter.augment(t);
    jsonWriter.writeTo(t.getModel(), JsonNode.class, genericType, annotations, mediaType, 
        httpHeaders, entityStream);
  }

}

With this the documentation links integrated in the HAL browser will work and link to a service documentation generated on the fly out of the documentation JSON metadata generated by the maven plugin and included in the bundle.

Publish JSON schema files for domain models

If you want to reference model classes with JSON schema files automatic generated from the HAL documentation those model files should be put to a separate bundle which contain only the model classes.

Add this plugins to the POM:

<plugin>
  <groupId>org.apache.felix</groupId>
  <artifactId>maven-bundle-plugin</artifactId>
  <configuration>
    <instructions>
      <Caravan-HalDocs-DomainPath>/myproject/mydomainmodels</Caravan-HalDocs-DomainPath>
    </instructions>
  </configuration>
</plugin>

<plugin>
  <groupId>io.wcm.caravan.maven.plugins</groupId>
  <artifactId>hal-docs-maven-plugin</artifactId>
  <executions>
    <execution>
      <goals>
        <goal>generate-json-schema</goal>
      </goals>
      <configuration>
        <includes>
          <include>my.project.models.*</include>
        </includes>
      </configuration>
    </execution>          
  </executions>
</plugin>

The include/exclude patterns define which package names contain the classes to generated the JSON schema files for.

The link between the bundle containing the RESTful service implementation and the bundle with the generated JSON schema files is detected automatically if a Maven compile dependency exists between them.

The JSON schema is displayed graphically with the Docson JavaScript widget within the documentation.

To add documentation to the domain model's properties use the Jackson @JsonPropertyDescription annotation. Example:

public final class Rel2Model {

  private String key;

  @JsonPropertyDescription("This is the description for the key property")
  public String getKey() {
    return this.key;
  }

  public Brochure setKey(String value) {
    this.key = value;
    return this;
  }

}

Back to top

Version: 0.6.1-SNAPSHOT. Last Published: 2024-03-21.