3.4. Metamodel (specs) Resource

The /specs/{fullyQualifedClassName} links indicated in several places in the representations produced by ObjectResource corresponds to the SpecsResource:

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

public interface SpecsResource {
  ...
}

The implementation of this interface in Restful Objects viewer (SpecsResourceImpl) also defines a @Path("/specs") for the class as a whole. This therefore defines a URL in the form /services supporting the GET method.

Note

I believe that the @Path annotation should reside on the interface, not the implementation. This seems to be a limitation with RestEasy 1.0.2, the underlying library used by Restful Objects.

The purpose of the /specs/ family of resources is to describe the structure of the domain objects, ie expose a metamodel for the domain objects. Client-side applications might choose to iterate through all the specs resources first and cache them; this would then simplify the task of rendering domain objects.

Again, let's break the resources provided by SpecsResource into sections.

3.4.1. All Classes

First up, we have a resource to list all classes (or specs):

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

public interface SpecsResource {

  @GET
  @Path("/")
  @Produces( { "application/xhtml+xml", "text/html" })
  public abstract String specs();

  ...
}

This defines /specs as a URL accepting GET. This returns the following:

The raw XHTML produced (abbreviated) is:

<div class="nof-section">
  <p class="nof-section">Specifications</p>
  <ul class="nof-specifications">
    ...
    <li>
      <a href="/specs/int" rel="spec" rev="specs" 
         class="nof-specification">int</a>
    </li>
    ...
    <li>
      <a href="/specs/java.lang.String" rel="spec" rev="specs" 
         class="nof-specification">java.lang.String</a>
    </li>
    ...
    <li>
      <a href="/specs/org.nakedobjects.applib.value.Date" rel="spec" rev="specs" 
         class="nof-specification">org.nakedobjects.applib.value.Date</a>
    </li>
    ..
    <li>
      <a href="/specs/org.nakedobjects.examples.claims.dom.claim.Claim" rel="spec" rev="specs" 
         class="nof-specification">org.nakedobjects.examples.claims.dom.claim.Claim</a>
    </li>
    <li>
      <a href="/specs/org.nakedobjects.examples.claims.dom.claim.ClaimItem" rel="spec" rev="specs" 
         class="nof-specification">org.nakedobjects.examples.claims.dom.claim.ClaimItem</a>
    </li>
    ...
    <li>
      <a href="/specs/void" rel="spec" rev="specs" 
         class="nof-specification">void</a>
    </li>
  </ul>
</div>

Useful XPath expressions:

  • //a[@class='nof-specification']/@href will give links to resources for all specifications

Let's look at an individual specification next.

3.4.2. Class (NakedObjectSpecification)

The resource for a specification is:

...

public interface SpecsResource {
  ...
  @GET
  @Path("/{specFullName}")
  @Produces( { "application/xhtml+xml", "text/html" })
  public abstract String spec(
    @PathParam("specFullName") final String specFullName);
  ...
}

This defines /specs/{specFullName} as a URL accepting GET. This returns:

The raw XHTML for this breaks into five regions.

First (abbreviated) we have the facets, which define additional semantics to the holder (in this case, the spec):

<div class="nof-facets">
  <p class="nof-facets">Facets</p>
  <table border="1">
     <tr>
       <th>FacetType</th>
       <th>Implementation</th>
       ...
     </tr>
     ...
     <tr>
       <td>
         <a href="org.nakedobjects.examples.claims.dom.claim.Claim/facet/org.nakedobjects.metamodel.facets.object.ident.plural.PluralFacet"
            rel="facet" rev="spec" class="nof-facet">org.nakedobjects.metamodel.facets.object.ident.plural.PluralFacet</a>
       </td>
       <td>
         <p>org.nakedobjects.metamodel.facets.object.ident.plural.PluralFacetInferred</p>
       </td>
       ...
     </tr>
     ...
     <tr>
       <td>
         <a href="org.nakedobjects.examples.claims.dom.claim.Claim/facet/org.nakedobjects.metamodel.facets.naming.named.NamedFacet"
            rel="facet" rev="spec" class="nof-facet">org.nakedobjects.metamodel.facets.naming.named.NamedFacet</a>
       </td>
       <td>
         <p>org.nakedobjects.metamodel.facets.naming.named.NamedFacetInferred</p>
       </td>
       ...
     </tr>
     ...
     <tr>
       <td>
         <a href="org.nakedobjects.examples.claims.dom.claim.Claim/facet/org.nakedobjects.metamodel.facets.naming.describedas.DescribedAsFacet" rel="facet" rev="spec" class="nof-facet">org.nakedobjects.metamodel.facets.naming.describedas.DescribedAsFacet</a>
       </td>
       <td>
         <p>org.nakedobjects.metamodel.facets.naming.describedas.DescribedAsFacetNone</p>
       </td>
       ...
     </tr>
     ...
   </table>
</div>

Many of the facets listed will not be that relevant to us, but some - such as the singular name of a class (NamedFacet), the plural name of a class (PluralFacet) and the description of a class (DescribedAsFacet) will be useful for presentation purposes. It is also possible to define additional facets that might be relevant to your own client-side application. For example, if you were writing a mash-up GUI and wanted to render an Address domain object within a map, you might want to define a MapCoordinatesFacet.

Note

Actually, this isn't quite true; there is currently no way to evaluate a facet for a particular domain object instance.

As ever, we can use XPath to pull out values:

  • //div[@class='nof-facets']//a[.='org.nakedobjects.metamodel.facets.object.ident.plural.PluralFacet']/@href will pull out the link for the PluralFacet, if there is one.

Next we have the (definition of the) properties for a spec:

<div class="nof-properties">
  <p class="nof-properties">Properties</p>
  <ul class="nof-properties">
    <li>
      <a href="/specs/org.nakedobjects.examples.claims.dom.claim.Claim/property/description" 
         rel="property" rev="spec" class="nof-property">description</a>
    </li>
    <li>
      <a href="/specs/org.nakedobjects.examples.claims.dom.claim.Claim/property/date" 
         rel="property" rev="spec" class="nof-property">date</a>
   </li>
   ...
  </ul>
</div>

Useful XPath queries here:

Similarly, we have collections:

<div class="nof-collections">
  <p class="nof-collections">Collections</p>
  <ul class="nof-collections">
    <li>
      <a href="/specs/org.nakedobjects.examples.claims.dom.claim.Claim/collection/items"
         rel="collection" rev="spec" class="nof-collection">items</a>
    </li>
    ...
  </ul>
</div>

Useful XPath queries here:

Lastly, the actions. These fall into three groups: regular (USER) actions, debug actions (annotated with @Debug) and exploration actions (that are available only in exploration mode):

<div class="nof-actions">
  <p class="nof-actions">USER actions</p>
  <ul class="nof-actions">
    <li>
      <a href="/specs/org.nakedobjects.examples.claims.dom.claim.Claim/action/addItem(int,double,java.lang.String)"
         rel="action" rev="spec" class="nof-action">addItem(int,double,java.lang.String)</a>
    </li>
    <li>
      <a href="/specs/org.nakedobjects.examples.claims.dom.claim.Claim/action/submit(org.nakedobjects.examples.claims.dom.claim.Approver)"
         rel="action" rev="spec" class="nof-action">submit(org.nakedobjects.examples.claims.dom.claim.Approver)</a>
    </li>
  </ul>
</div>
<div class="nof-actions">
  <p class="nof-actions">DEBUG actions</p>
  <ul class="nof-actions" />
</div>
<div class="nof-actions">
  <p class="nof-actions">EXPLORATION actions</p>
  <ul class="nof-actions" />
</div>

Useful XPath queries here:

  • //div[@class='nof-actions' and p/text()='USER actions']//a/@href returns links to the regular user actions

  • //div[@class='nof-actions' and p/text()='DEBUG actions']//a/@href returns links to the debug actions

  • //div[@class='nof-actions' and p/text()='EXPLORATION actions']//a/@href returns links to the exploration actions

3.4.3. Class Members (NakedObjectMember)

The next set of resources provided by SpecsResource are for the individual class members (properties, collections or actions):

...

public interface SpecsResource {
  ...
  @GET
  @Path("/{specFullName}/property/{propertyName}")
  @Produces( { "application/xhtml+xml", "text/html" })
  public abstract String specProperty(
    @PathParam("specFullName") final String specFullName,
    @PathParam("propertyName") final String propertyName);

  @GET
  @Path("/{specFullName}/collection/{collectionName}")
  @Produces( { "application/xhtml+xml", "text/html" })
  public abstract String specCollection(
    @PathParam("specFullName") final String specFullName,
    @PathParam("collectionName") final String collectionName);

  @GET
  @Path("/{specFullName}/action/{actionId}")
  @Produces( { "application/xhtml+xml", "text/html" })
  public abstract String specAction(
    @PathParam("specFullName") final String specFullName,
    @PathParam("actionId") final String actionId);
  ...
}

This defines the following URLs all accepting GET:

  • /specs/{specFullName}/property/{propertyName} for a resource representing a property definition

  • /specs/{specFullName}/collection/{propertyName} for a resource representing a collection definition

  • /specs/{specFullName}/action/{actionId} for a resource representing a action definition

Each of these resources generates a similar representation, listing the facets for that class member. For example, here is the resource for a property spec:

The raw XHTML (abbreviated) is:

<div>
  <p>Owners</p>
  <ul class="nof-specification">
    <a href="/specs/org.nakedobjects.examples.claims.dom.claim.Claim" rel="spec" rev="property" class="nof-specification">owning spec</a>
  </ul>
</div>
<div class="nof-facets">
  <p class="nof-facets">Facets</p>
  <table border="1">
    <tr>
      <th>FacetType</th>
      <th>Implementation</th>
      ...
    </tr>
    <tr>
      <td>
        <a href="description/facet/org.nakedobjects.metamodel.facets.propparam.typicallength.TypicalLengthFacet" rel="facet" rev="spec" class="nof-facet">org.nakedobjects.metamodel.facets.propparam.typicallength.TypicalLengthFacet</a>
      </td>
      <td>
        <p>org.nakedobjects.metamodel.facets.propparam.typicallength.TypicalLengthFacetDerivedFromType</p>
      </td>
      ...
    </tr>
    ...
    <tr>
      <td>
        <a href="description/facet/org.nakedobjects.metamodel.facets.ordering.memberorder.MemberOrderFacet" rel="facet" rev="spec" class="nof-facet">org.nakedobjects.metamodel.facets.ordering.memberorder.MemberOrderFacet</a>
      </td>
      <td>
        <p>org.nakedobjects.metamodel.facets.ordering.memberorder.MemberOrderFacetAnnotation</p>
      </td>
      ...
    </tr>
    <tr>
      <td>
        <a href="description/facet/org.nakedobjects.metamodel.facets.propparam.validate.mandatory.MandatoryFacet" rel="facet" rev="spec" class="nof-facet">org.nakedobjects.metamodel.facets.propparam.validate.mandatory.MandatoryFacet</a>
      </td>
      <td>
        <p>org.nakedobjects.metamodel.facets.propparam.validate.mandatory.MandatoryFacetDefault</p>
      </td>
      ...
    </tr>
    ...
  </table>
</div>

As for specifications themselves (see Section 3.4.2, “Class (NakedObjectSpecification)”), many of the facets will not be that relevant. However, NamedFacet and DescribedAsFacet mentioned earlier would be, for presentation purposes. In addition, for properties the TypicalLengthFacet can be used as a hint for a field in the UI, and MemberOrderFacet annotation can be used to indicate the order of fields in the UI. The MandatoryFacet can be used to indicate mandatory properties.

As ever, XPath can be used to pull out information from the resource.

Note

Although actions produce a similar output, they ought to be extended to provide information on action parameters; both how many parameters there are, and also facets associated with those parameters.

3.4.4. Facets

A facet resource allows the value of a facet to be inspected. The resources provided by SpecsResource are:

...

public interface SpecsResource {
  ...
  @GET
  @Path("/{specFullName}/facet/{facetType}")
  @Produces( { "application/xhtml+xml", "text/html" })
  public abstract String specFacet(
    @PathParam("specFullName") final String specFullName,
    @PathParam("facetType") final String facetTypeName);

  @GET
  @Path("/{specFullName}/property/{propertyName}/facet/{facetType}")
  @Produces( { "application/xhtml+xml", "text/html" })
  public abstract String specPropertyFacet(
    @PathParam("specFullName") final String specFullName,
    @PathParam("propertyName") final String propertyName,
    @PathParam("facetType") final String facetTypeName);

  @GET
  @Path("/{specFullName}/collection/{collectionName}/facet/{facetType}")
  @Produces( { "application/xhtml+xml", "text/html" })
  public abstract String specCollectionFacet(
    @PathParam("specFullName") final String specFullName,
    @PathParam("collectionName") final String collectionName,
    @PathParam("facetType") final String facetTypeName);

  @GET
  @Path("/{specFullName}/action/{actionId}/facet/{facetType}")
  @Produces( { "application/xhtml+xml", "text/html" })
  public abstract String specActionFacet(
    @PathParam("specFullName") final String specFullName,
    @PathParam("actionId") final String actionId,
    @PathParam("facetType") final String facetTypeName);
  ...
}

This defines the following URLs all accepting GET:

  • /specs/{specFullName}/facet/{facetType} for a facet on a spec

  • /specs/{specFullName}/property/{propertyName}/facet/{facetType} for a facet on a property

  • /specs/{specFullName}/collection/{propertyName}/facet/{facetType} for a facet on a collection

  • /specs/{specFullName}/action/{actionId}/facet/{facetType} for a facet on an action

Note

In addition we need a resource to allow the facets of an actions parameter to be queried.

Note

(Mentioned elsewhere), we also need to provide the ability to evaluate facets per instance. Although many facets are per class, some (such as TitleFacet) will vary by instance. This will (presumably) need some additional resource methods (eg specActionFacetFor(...)) that take an oid as a parameter.

Each of these resources generates a similar representation, evaluating a facets for its facet holder. For example, here is the resource for a property spec:

The raw XHTML (abbreviated) is:

<div>
  <p>Owners</p>
  <ul class="nof-properties">
    <li>
      <a href="/specs/org.nakedobjects.examples.claims.dom.claim.Claim" rel="owning spec" 
         rev="spec" class="facet">
        org.nakedobjects.examples.claims.dom.claim.Claim
      </a>
    </li>
    <li>
      <a href="/specs/org.nakedobjects.examples.claims.dom.claim.Claim/property/description"
         rel="owning property" rev="property" class="facet">
        description
      </a>
    </li>
  </ul>
</div>
<div class="nof-facet-elements">
  <p class="nof-facet-elements">Facet Elements</p>
  <dl class="nof-facet-elements">
    <dt>class</dt>
    <dd>class org.nakedobjects.metamodel.facets.propparam.typicallength.TypicalLengthFacetDerivedFromType</dd>
    <dt>derived</dt>
    <dd>false</dd>
    <dt>facetHolder</dt>
    <dd>Reference Association [name="org.nakedobjects.examples.claims.dom.claim.Claim#description(), type=JavaSpecification@9c22ff[class=java.lang.String,type=Object,persistable=User Persistable,superclass=Object] ]</dd>
    <dt>identified</dt>
    <dd>Reference Association [name="org.nakedobjects.examples.claims.dom.claim.Claim#description(), type=JavaSpecification@9c22ff[class=java.lang.String,type=Object,persistable=User Persistable,superclass=Object] ]</dd>
    <dt>noop</dt>
    <dd>false</dd>
    <dt>underlyingFacet</dt>
    <dd>(null)</dd>
  </dl>
</div>

This representation is created by applying a JavaBean conventions on the facet (all getXxx() and isXxx() methods exposed by the facet itself).

Note

We need a more general purpose mechanism to query facets. As can be seen, the above doesn't actually expose the typical value of the facet, because the method we want is called value(), not getValue().

Some XPath queries that might be useful are:

  • //div[@class='nof-facet-elements']//dt[.='class']/following-sibling::dd[1] will return the <dd> element corresponding to the 'class' <dt>

  • //div[@class='nof-facet-elements']//dt[.='derived']/following-sibling::dd[1] will return the <dd> element corresponding to the 'derived' <dt>d