Skip to content

UsingPermazenForRPC

Archie L. Cobbs edited this page Jan 11, 2024 · 6 revisions

Permazen can be thought of as Java object serialization solution. This makes it handy for doing RPC between systems.

But you're not just sending serialized data, you're sending a database transaction. So you benefit from all of the features of a transaction: index queries, schema migration, validation, etc.

The schema-aware nature of Permazen means the server and client versions can be loosely bound, and objects sent between them will be automatically migrated from the sending system's schema version to the receiving system's schema version (see @OnVersionChange).

To set this up, you need to:

  1. Define the Java model that will represent the data to be sent back & forth
  2. Define a Permazen database from those Java classes

When you're ready to send data, create a detached (i.e., in-memory) transaction from the Permazen database, populate the transaction with the data to send, serialize the underlying key/value pairs (this is trivial), and send it across.

If you're building a Spring application, the PermazenObjectHttpMessageConverter handles all the details for you. If not using Spring, you can refer to that class for the mechanics.

Here's how you would set this up with Spring:

    <!-- Define RestTemplate for the REST API client -->
    <bean id="clientRestTemplate" class="org.springframework.web.client.RestTemplate">
        <property name="messageConverters">
            <util:list>
                <bean class="io.permazen.spring.PermazenObjectHttpMessageConverter" c:jdb-ref="myPermazenInstance"/>
            </util:list>
        </property>
    </bean>

Your client-side Java code might look something like this:

import io.permazen.Permazen;
import io.permazen.PermazenTransaction;
import io.permazen.ValidationMode;

import java.net.URI;

import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;

@Configurable
public class MyClient {

    @Autowired
    private Permazen restPermazen;

    @Autowired
    private RestTemplate restTemplate;
    
    private URI restURI;        // set this somehow

    public MyResponse sendRequest() throws RestClientException {

        // Create an in-memory transaction
        final PermazenTransaction jtx = this.restPermazen.createDetachedTransaction();

        // Populate it with request object(s)
        final MyRequest request = jtx.create(MyRequest.class);
        // ... configure request, add associated objects, etc...

        // Send to server and decode response (contained in another in-memory transaction)
        return this.restTemplate.postForObject(this.restURI, request, MyResponse.class);
    }
}

On the server side, you would configure a PermazenObjectHttpMessageConverter to deserialize incoming requests.

Note in the example below, we are enabling validation. This means by the time your Java code processes the request, you can safely assume every object in the entire transaction has already been validated.

    <!-- Message converter for REST requests and responses -->
    <bean id="restMessageConverter" class="io.permazen.spring.PermazenObjectHttpMessageConverter" c:jdb-ref="restPermazen">
        <property name="validationGroups">
            <util:list/>  <!-- an empty list means use the default validation group -->
        </property>
    </bean>

    <!-- Spring MVC config -->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <ref bean="restMessageConverter"/>
        </mvc:message-converters>
    </mvc:annotation-driven>

Now that Spring knows how to deserialize your REST data model objects, your server-side code might look like this:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class RestController {

    @Autowired
    private Permazen restPermazen;

    @RequestMapping(value = "/myuri", method = RequestMethod.POST)
    @ResponseBody
    public MyResponse handleRequest(HttpServletRequest httpRequest,
      HttpServletResponse httpResponse, @RequestBody MyRequest request) throws IOException {

        // Create an in-memory transaction
        final PermazenTransaction jtx = this.restPermazen.createDetachedTransaction();

        // Populate it with response object(s)
        final MyResponse response = jtx.create(MyResponse.class);
        // ... configure response, add associated objects, etc...

        // Send back to client
        return response;
    }
}