You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 4 Next »

Writing a SPIN Query
A SPIN network is a collection of nodes, which are objects that can perform queries. SPIN nodes join together to form networks that allow queries to be broadcast from node to node and the results are then aggregated in the reverse order. 

Nodes can be accessed directly by code running in the same JVM, or exposed over HTTP via an application server (Clint – do you really mean app server or servelet container)

This document explains how to write a SPIN plug-in, which is used to implement a query, and it provides client code that can be used to perform this query remotely.

Resources

System Requirements

  • Sun/Oracle Java Development Kit (JDK) 1.6.0_04 or later.  Other JDKs, including OpenJDK, are not supported but will likely work.
  • Apache Maven 2.2.1. Maven 3.x is not supported, but will likely work. (Required to build SPIIN and the example module)

Hello, Spin

* *The queries that SPIN nodes perform are implemented as instances of the QueryAction interface.

public interface QueryAction<Criteria>

{

    String perform(final QueryContext context, final Criteria criteria) throws QueryException;


    Criteria unmarshal(final String serializedCriteria) throws SerializationException;


    boolean isReady();


    void destroy();

}

https://scm.chip.org/svn/repos/spin/base/trunk/node/api/src/main/java/org/spin/node/actions/QueryAction.java

QueryActions takes a serialized input criteria and returns serialized results.  It is up to the implementer to choose a serialization format, if any.

Queries are submitted to a SPIN network using either the Agent or Querier class.  The Querier class is higher level, Itis used in this document and the examples module.

Implement QueryAction

A simple QueryAction is one that echoes its input.  Here's how it would look in Scala:

final class EchoQueryAction extends QueryAction[String] {

    override def unmarshal(serialized: String) = serialized


    override def perform(context: QueryContext, input: String) = input


    override def isReady = true


    override def destroy { }

}

This could be simplified by extending AbstractQueryAction, which provides default implementations for isReady() and destroy() which are suitable for a stateless QueryAction like EchoQueryAction:

final class EchoQueryAction extends AbstractQueryAction[String] {

    override def unmarshal(serialized: String) = serialized


    override def perform(context: QueryContext, input: String) = input

}
A QueryAction's unmarshal method defines how the input criteria is unmarshalled into a Java object.  In this case, we  just echoing our input, so we don't need to deserialize. 

Consider a slightly more complicated QueryAction, that receives as input a list of integers and returns the sum.  Here the serialization format is XML, so we can extends JAXBQueryAction, which supplies an implementation of unmarshal() that uses JAXB to turn raw XML into a object in the JVM:

final class AddQueryAction extends JAXBQueryAction(classOf[AddInput]) {

    //Take an AddInput, and return an XML-serialized AddResult

    override def perform(context: QueryContext, input: AddInput): String = {

        val result = new AddResult(input.toAdd.sum)


        JAXBUtils.marshalToString(result)

    }

}

This class takes input like:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<ns2:AddInput xmlns:ns2="http://spin.org/xml/res/">

    <number xsi:type="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">1</number>

    <number xsi:type="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2</number>

    <number xsi:type="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3</number>

</ns2:AddInput>

which gets unmarshalled into a class like

@XmlAccessorType(XmlAccessType.FIELD)

@XmlType(name = "AddInput", namespace = "http://spin.org/xml/res/")

@XmlRootElement(name = "AddInput", namespace = "http://spin.org/xml/res/")

//The only field is a Java List, which works with JAXB

final case class AddInput(private val number: JList[Int])

Unknown macro: {     //JAXB requires a no-arg constructor; can be private     *def* *this*() = *this*(*new* JArrayList[Int])     //For nicer Scala interop     *def* *this*(toAdd}

by JAXB.

Returning Results

QueryActions return results as serialized Strings.  Spin is agnostic about the contents of the returned String, and, as with input, the serialization format used. 

In this case, if we define a class that's serializable by JAXB, like:

@XmlAccessorType(XmlAccessType.FIELD)

@XmlType(name = "AddResult", namespace = "http://spin.org/xml/res/")

@XmlRootElement(name = "AddResult", namespace = "http://spin.org/xml/res/")

final case class AddResult(val sum: Int)

Unknown macro: {     //JAXB requires a no-arg constructor; can be private     *def* *this*() = *this*(0) }

We can use Spin's JAXBUtils class to easily serialize an instance into a String:

final class AddQueryAction extends JAXBQueryAction(classOf[AddInput]) {

    //Take an AddInput, and return an XML-serialized AddResult

    override def perform(context: QueryContext, input: AddInput): String = {

        val result = new AddResult(input.toAdd.sum)

        JAXBUtils.marshalToString(result)

    }

}

  • No labels