Writing a Spin Query Objective and overview This guide will explain ...

grapedraughtΛογισμικό & κατασκευή λογ/κού

2 Δεκ 2013 (πριν από 3 χρόνια και 4 μήνες)

77 εμφανίσεις

Writing a Spin Query


Objective and overview


This guide will explain how to write a Spin plugin to implement a query, and client
code to perform this query remotely.


Resources



Spin sources:
h
ttps://scm.chip.org/svn/repos/spin/base/trunk/



Example Spin extension, in Java and Scala:
https://scm.chip.org/svn/repos/spin/base/trunk/examples


Dependencies



A Sun/Oracle Java Devel
opment Kit with version greater than or equal to
1.6.0_04 is required. Other JDKs, including OpenJDK, are not supported but
will likely work; we make no guarantees.



To build Spin and the examples module, Apache Maven 2.2.1 is required. Maven
3.x is not s
upported, but will likely work; we make no guarantees.


Hello, Spin


High
-
level overview


A Spin network is a collection of Nodes, objects which can perform queries. Nodes
can join together to form networks, where queries are broadcast from one node to
ot
hers, and results are aggregated in the reverse order. Nodes may be accessed
directly by code running in the same JVM, or exposed over HTTP via an app server.


The queries that nodes can 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();



v
oid

destroy();

}


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



Qu
eryActions take a serialized input criteria and return 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, and is used in this guide and the examples
module.


Implement QueryAction


An extremely 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 p
rovides 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're just echoing our input, so we don't need
to deserialize
. If the input was, say, XML in a form that JAXB can convert to an
instance of some class Foo, we could simplify further extending JAXBQueryAction:


final

class

EchoQueryAction

extends

JAXBQueryAction(classOf[Foo])

{


override

def

perform(context:

Quer
yContext,

input:

Foo)

=

input.toString


//serialize results in the simplest, dumbest way, with toString()

}


Make a Node that loads your query


A Spin node is an instance of the SpinNodeImpl class, accessed either directly in
the same JVM, or remotely v
ia HTTP. For this example, we'll use the in
-
JVM method,
because it's simplest.


Nodes assign each query a string identifier, called a QueryType, which is used to
look up the QueryAction class that implements the query. This mapping can be set
up one of t
wo ways:


Specify QueryAction class directly


Create a NodeConfig object with the mapping like so:


val

queryType

=

"Spin.Example.Echo"

val

queryActionClassName

=

classOf[EchoQueryAction].getName



val

echoNodeConfig

=

NodeConfig.Default.withQuery(
new

Q
ueryTypeConfig(queryType,

queryActionClassName))


echoNodeConfig may be passed to SpinNodeImpl's constructor:


val

nodeID

=

...

val

routingTable

=

...

val

node

=

new

SpinNodeImpl(nodeID,

echoNodeConfig,

routingTable)


Use a QueryActionMap


Specifying Query
Type
-
to
-
QueryAction
-
class mappings directly is straightforward, but
has some drawbacks. First, the QueryAction implementation must have a public no
-
argument constructor. Second, the Spin extension implementer has less control over
the lifecycle of the Qu
eryAction


QueryActions will be instantiated when the Node
starts, and their destroy() methods will be called when the Node is shut down, but
no further guarantees are possible. Caching, lazy
-
loading, memoization, or other
techniques may be performed by
the Spin Node, but this is out of the extension
implementer's hands.


Alternatively, one can supply an instance of the QueryActionMap interface. This
interface describes a factory for QueryActions that the Node uses to obtain them.
A QueryActionMap may b
e lazy, cache QueryActions or not, or use a DI framework like
Guice or Spring, among other possibilities. Defining a QueryActionMap, while
fairly straightforward, is more verbose than the direct
-
mapping approach and will
not be used in this guide.


Make a

querier


Spin's client
-
side classes are fairly low
-
level. Here's a higher
-
level wrapper
that makes use of them to synchronously send a query to a node and get the results:


object

Config

{


val

credentials

=

new

Querier.Credentials(
"CBMI"
,

"echo
-
user"
,

"echo
-
password"
)



val

identityService

=

AlwaysCertifiesIdentityService.Instance



val

peerGroupName

=

"EchoPeerGroup"



val

nodeName

=

"EchoNode"



val

agentConfig

=

AgentConfig.Default.withPeerGroupToQuery(peerGroupName).withPollingFrequenc
y(10.0F)




val

routingTableConfig

=

new

RoutingTableConfig(
new

PeerGroupConfig(peerGroupName))




val

queryType

=

"Spin.Examples.Echo"




val

nodeConfig

=

NodeConfig.Default.withQuery(
new

QueryTypeConfig(queryType,

classOf[EchoQueryA
ction].getName))

}




final

class

EchoQuerier

private

(querier:

Querier)

{



def

this
(toBeQueried:

SpinNode)

=

this
(
new

Querier(Config.agentConfig,

Config.identityService,

NodeConnector.instance(toBeQueried)))




def

query(messageToBeEchoed:

Strin
g):

Option[String]

=

{



try

{


val

results

=

querier.query(Config.queryType,

Config.credentials,

messageToBeEchoed)




if
(!results.isEmpty)

Some(results.get(0))

else

None


}


catch

{


case

e:

TimeoutException

=>

{

println(
"Timed out waiting for query to complete: "

+

e.getMessage)

;

e.printStackTrace(System.err)

;

None

}



case

e:

AgentException

=>

{

println(
"Error making query: "

+

e.getMessage)

;

e.printStackTrace(System.err)

;

None}


}


}

}


Test


//TODO: unit test that invokes EchoQuerier


Inside the JVM


In an app server


Obtain the Spin node war


Install Spin


Configure Spin


Resources