Hints and Tips

This chapter provides some solutions for problems we’ve encountered ourselves or have been raised on the Apache Isis mailing lists.

Since the Restful Objects viewer is designed for computer programs to interact with (rather than human beings), it can be a little difficult to explore and generally "grok" how it works.

This section provides a few hints-and-tips to help you on your way.

Pretty printing

The JSON representations generated by the Restful Objects viewer are in compact form if the deployment type is SERVER (ie production), but will automatically be "pretty printed" (in other words indented) if the deployment type is PROTOTYPE.

How parse images in RO viewer?

From this thread on the Apache Isis users mailing list:

  • I am trying to display an image in a JavaScript client app, the image comes from an Isis RO web service as a string, but it won’t show. Is there something I should do to change the message?

The RO viewer returns the image as a string, in the form:

"Tacos.jpg:image/jpeg:/9j//4AAQSkZJRgABAQEAlgCWAAD/  ...."

This is in the form:

(filename):(mime type):(binary data in base64)

This is basically the Blob value type, in string form.

To use, split the parts then format the mime type and base64 data correctly before using as source in an <img> tag.

View Model as Parameter

As discussed on the mailing list.

Query

I must provide a REST service accepting more complex view model as input parameter.

My view model parameter would look like

@DomainObject(
    nature = Nature.VIEW_MODEL,
    logicalTypeName = "OfferTemplateFilter"
)
@XmlRootElement(name = "OfferTemplateFilter")
@XmlAccessorType(XmlAccessType.FIELD)
@Getter @Setter
public class OfferTemplateFilter {
    public List<String> selectedDeviceManufacturer = new ArrayList<>();
    public List<String> selectedDeviceSizes = new ArrayList<>();
}

My REST domain service would be someting like

@DomainService(
    nature = NatureOfService.VIEW_REST_ONLY,
    logicalTypeName = "OfferRestService"
)
public class OfferRestService {

    @Action(semantics = SemanticsOf.IDEMPOTENT)
    public OfferTemplateSelectorForCustomer
        offerSelectorForCustomer(
            final String subscriberNumber,
            final OfferTemplateFilter filter) {
        return offerSelectorRepository.create(subscriberNumber, filter);
    }
    ...
}

I’m wondering how this could be achieved without custom rest service. Ideally the service consumer would post a kind of JSON structure where my view model OfferTemplateFilter would be created?

Possible Answer…​

Rather than try to "upload" the OfferTemplateFilter view model as a parameter, instead treat it as a resource.

That is:

  • have a new service to create an instance of the filter, and then

  • update this filter (adding/removing from its two collections).

  • When done, pass a reference to the filter to the original REST service, as a regular reference.

Obviously the URL passed in the last step will be rather long and messy, but that’s not a problem per-se.

Troubleshooting

Exception

When invoking a REST service and getting a response with an embeded stacktrace like:

{
	"httpStatusCode": 500,
	"message": "com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Klasse enthält zwei Eigenschaften mit demselben Namen \"name\"
	this problem is related to the following location:
		at public java.lang.String ife.dep.ServiceName.getName()
		at ife.dep.ServiceName
		at public ife.dep.ServiceName ife.dep.ProvidedService.getServiceName()
		at ife.dep.ProvidedService
		at public java.util.SortedSet ife.dep.Release.getCapabilities()
		at ife.dep.Release
		at public ife.dep.Release ife.cfg.Configuration.getRelease()
		at ife.cfg.Configuration
		at public ife.cfg.Configuration ife.cfg.ConfigEntry.getConfiguration()
		at ife.cfg.ConfigEntry
		at public java.util.SortedSet ife.cfg.Endpoint.getCfgEntries()
		at ife.cfg.Endpoint
	this problem is related to the following location:
		at public java.lang.String ife.dep.ServiceName.name
		at ife.dep.ServiceName
		at public ife.dep.ServiceName ife.dep.ProvidedService.getServiceName()
		at ife.dep.ProvidedService
		at public java.util.SortedSet ife.dep.Release.getCapabilities()
		at ife.dep.Release
		at public ife.dep.Release ife.cfg.Configuration.getRelease()
		at ife.cfg.Configuration
		at public ife.cfg.Configuration ife.cfg.ConfigEntry.getConfiguration()
		at ife.cfg.ConfigEntry
		at public java.util.SortedSet ife.cfg.Endpoint.getCfgEntries()
		at ife.cfg.Endpoint
",
[...]
}

Solution

Add @XmlAccessorType(XmlAccessType.FIELD) to your domain entity:

@javax.jdo.annotations.PersistenceCapable(identityType = IdentityType.DATASTORE)
@javax.jdo.annotations.DatastoreIdentity(strategy = javax.jdo.annotations.IdGeneratorStrategy.NATIVE, column = "id")
@javax.jdo.annotations.Version(strategy = VersionStrategy.VERSION_NUMBER, column = "version")
@DomainObject(logicalTypeName = "ServiceName", bounded = true)
@DomainObjectLayout(cssClassFa = "tag", describedAs = "")
@XmlAccessorType(XmlAccessType.FIELD)
@SuppressWarnings("javadoc")
public class ServiceName implements Capability, Comparable<ServiceName> {

	public String title() {
		return getName();
	}

	public int compareTo(ServiceName o) {
		return name.compareTo(o.name);
	}

	@Override
	public String toString() {
		return title();
	}

	@javax.jdo.annotations.Column(allowsNull = "false")
	@Getter	@Setter
	public String name;

	public boolean isSynchronous() {
		return !isAsynchonous();
	}

	public boolean isAsynchonous() {
		return name.startsWith(Constants.ESB_JMS_PREFIX);
	}
}