...
Code Block |
---|
@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) { //JAXB requires a no-arg constructor; can be private def this() = this(0) } |
You can use SPIN's JAXBUtils class to serialize an instance into a String:
Code Block |
---|
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 sum = input.toAdd.sum val result = new AddResult(input.toAdd.sum) JAXBUtils.marshalToString(result) } } |
Make a node that loads your query
...
Code Block |
---|
object Config { //The human-readable name of the node we will create; purely descriptive val nodeName = "AddNode" //Some dummy credentials for submitting queries with val credentials = new Credentials("CBMI", "some-user", "some-password") //Method that returns a SpinClientConfig with all necessary fields set to enable //querying the passed -in node def spinClientConfigFor(node: SpinNode) = { val nodeConnectorSource = registerAsLocalSource(nodeConnectorSourceFor(node)) val entryPoint = new EndpointConfig(EndpointType.Local, nodeName) SpinClientConfig.Default.withCredentials(credentials).withNodeConnectorSource(nodeConnectorSource).withEntryPoint(entryPoint) } //Nodes may participate in one or more overlay networks, called peer groups. By belonging to more //than one peer group, the same node can exist at different points in different logical network topologies. val peerGroupName = "AddPeerGroup" //An empty, dummy, routing table. Defines one peer group, 'AddPeerGroup' that only the node //we will create belongs to. val routingTableConfig = new RoutingTableConfig(new PeerGroupConfig(peerGroupName)) //The QueryType name that will map to the AddQueryAction class. Clients specify which query they would //like to perform by specifying a QueryType. val queryType = "Spin.Add" //Create a NodeConfig with default values for its fields, and a mapping between the QueryType 'Spin.Add' and //an instance of AddQueryAction val nodeConfig = NodeConfig.Default.withQuery(new QueryTypeConfig(queryType, classOf[AddQueryAction].getName)) //Needed to enable locating node instances when making in-JVM queries private def nodeConnectorSourceFor(node: SpinNode) = new NodeConnectorSource { override def getNodeConnector(endpoint: EndpointConfig, timeoutPeriod: Long): NodeConnector = NodeConnector.instance(node) } //Needed to enable locating node instances when making in-JVM queries private def registerAsLocalSource(source: NodeConnectorSource): NodeConnectorSource = { NodeOperationFactory.addMapping(EndpointType.Local, source) source } } |
Here is a higher-level wrapper that makes use of the information from Config to synchronously send a query to a node and get the results:
Code Block |
---|
final class AddClient(client: SpinClient) { //Init with the in-JVM node we're going to query def this(toBeQueried: SpinNode) = this(new SpinClient(Config.spinClientConfigFor(toBeQueried))) //Take a bunch of ints, return an Option of their sum, or None if there was an error. def query(intsToBeAdded: Seq[Int]): Option[Int] = { //method to extract the first (and in this case, only) Result from a ResultSet def firstAddResult(resultSet: ResultSet) = JAXBUtils.unmarshal(resultSet.getResults.head.getPayload.getData, classOf[AddResult]) try { //Actually make the query val resultSet = client.query(Config.peerGroupName, Config.queryType, new AddInput(intsToBeAdded)) log(resultSet) //Inspect the results, and extract the first (only) one if(resultSet.size > 0) Some(firstAddResult(resultSet).sum) else None } catch { case e: TimeoutException => { println("Timed out waiting for query to complete: " + e.getMessage) ; e.printStackTrace(System.err) ; None } case e: Exception => { println("Error making query: " + e.getMessage) ; e.printStackTrace(System.err) ; None} } } private def log(resultSet: ResultSet) { val expectedString = Option(resultSet.getTotalExpected).map(_.toString).getOrElse("?") val completeString = if(resultSet.isComplete) "complete" else "incomplete" println("(" + completeString + ": " + resultSet.size + "/" + expectedString + " results)") } } |
Here is a main method that ties it all together:
Code Block |
---|
object Main { def main(args: Array[String]) = { //Create a node to query; once instantiated, it is ready to be queried val node = new SpinNodeImpl(new CertID("123456789", "some-node"), //arbitrary ID Config.nodeConfig, //pre-made config, references AddQueryAction RoutingTableConfigSources.withConfigObject(Config.routingTableConfig)) //pre-made config, contains one peer group //Create a client pointing at that node val client = new AddClient(node) val prompt = "Add> " val in = new BufferedReader(new InputStreamReader(System.in)) println("Enter a list of numbers to be summed, separated by spaces or commas") println("Enter quit to exit") print(prompt) var line = in.readLine while(line != null) { //Shut down cleanly if requested if(line.equalsIgnoreCase("quit")) { println("Exiting...") node.destroy() System.exit(0) } if(!line.isEmpty) { //Split each line on non-numeric chars, and turn the chunks into ints val numbers = line.trim.split("[^\\d]+").map(Integer.parseInt) //Query the node we made earlier val queryResult = client.query(numbers) //Print the results println(queryResult.map(result => "Received: '" + result + "'").getOrElse("Query failed")) } print(prompt) line = in.readLine } } } |
Running this will create an in-JVM Spin node and configure it to respond to the 'Spin.Add' QueryType using an AddQueryAction instance; create a client that queries that node and performs queries in response to user input. These examples are available at:http://scm.chip.org/svn/repos/spin/base/trunk/examples
...
Code Block |
---|
val entrypoint = new EndpointConfig(EndpointType.SOAP, "http://localhost:8080/examples/node") val config = SpinClientConfig.Default.withEntryPoint(entrypoint) val client = new SpinClient(config) |
...
Code Block |
---|
<definitions targetNamespace="http://org.spin.node/"> <types> <xsd:schema> <xsd:import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="http://localhost:8080/examples/node?xsd=1"/> </xsd:schema> <xsd:schema> <xsd:import namespace="http://spin.org/xml/res/endpoint" schemaLocation="http://localhost:8080/examples/node?xsd=2"/> </xsd:schema> <xsd:schema> <xsd:import namespace="http://org.spin.node/" schemaLocation="http://localhost:8080/examples/node?xsd=3"/> </xsd:schema> </types> |
From the examples-war module, run the class org.spin.examples.Test. It has a main method that will connect to a Spin node running in a servlet container that's configured to expose the AddQueryAction class from the examples-scala module.
...