Introduction
News
Search
Documentation
License
Download
Contributors
Contact Us
|
Antiquity
Secure Log For Wide-Area Distributed Storage
|
Programmer's Guide
|
- Programmer's Guide - Detailed Explanations of Bamboo and Bamboo Applications From A Programmer's Perspective
- Introduction
- Note:
- Read the papers below as a basis to understand the structure and coding style used before actually coding
- Event-Driven Programming Style:
- Bamboo's Programmer Tutorial - Programming snippets to demonstrate different event-driven functionality
- More information on Currying, a function used in the Bamboo Programmer Tutorial is located in its wiki
- Programming a Bamboo Application
- External Data Representation (XDR) - RFC File
- "XDR is a standard for the description and encoding of data. It is useful for transferring data between different computer architectures, and has been used to communicate data between diverse machines" (http://ietf.org/rfc/rfc1832.txt)
- XDR files (end in .x) are very similar to header files in the programming languages of C and C++
- The language used to write XDR files is actually very similar to C and C++, so knowing either language is enough to write the majority of the file
- Registering event RPC's in an XDR File:
- Recall:
- For any event used between stages, an RPC needs to be registered for it in order to be a mutual function for all stages involved in the application
- Each RPC is identified by a triple:
- Identifier: (ProgramNumber, VersionNumber, ProcedureNumber), as defined in the XDR RFC file
- For every event RPC that is going to be registered in the application there are 2 requirements:
- Arguments - the required data to execute the event
- Results - the result of the RPC, usually expressed by a status message of success or failure
- Both requirements are written in the form of structures:
- i.e.
- 1 enum PingStatus
- 2 {
- 3 PING_STATUS_OK = 0,
- 4 PING_STATUS_ERROR = 1
- 5 };
- 6
- 7 struct ping_args
- 8 {
- 9 String msg;
- 10 };
- 11
- 12 struct ping_result
- 13 {
- 14 PingStatus status;
- 15 String msg;
- 16 };
- When compiling (through the Makefile, discussed in the Programming Tutorial) jrpcgen, a class generator, takes each of the structures and generates the associated java classes for it
- Below is what is placed at the end of the XDR file. This maps the above event's RPC arguments and results to the RPC identifier
- 17 program PING_API
- 18 {
- 19 version PING_API_VERSION
- 20{
- 21 void
- 22 ping_null(void) = 1001;
- 23
- 24 ping_result
- 25 ping(ping_args) = 1002;
- 26 } = 1;
- 27 } = 0x00002112;
- The configuration file (ends in .cfg) -
- Note:
- See "The configuration file" section in the User's Guide for further explanation on the file itself
- Recall:
- This is a file that contains any pre-defined settings, necessary for loading the Bamboo application and the stages used in the application
- Java Class
- Note:
- In the example lightly being used throughout this programmer's guide, the ping request from client to server, the following descriptions of the Java class fully pertain to both client (Requester) and server (Replier); however, the only discrepancy between requester & replier's is the handleEvent(QueueElementIF event) due to the client stage making the request and the server stage replying to the request
- Since we use the StandardStage to conform with the Bamboo system, we must extend it to allow functionality with Bamboo
- i.e. public class Server extends ostore.util.StandardStage
- init(ConfigDataIF config) - Initialize (primarily used as the constructor)
- event types[] array
- Here is where the classes of the events we want to listen to, are stored
- The classes in this array will eventually be passed to handleEvent()
- Primarily, the class:
seda.sandStorm.api.StagesInitializedSignal is used to notify us that all the stages we are using have run their init() and the Bamboo system is ready for use (usually noted with a boolean)
- super.init(config)
- Passes the configuration information up to StandardStage (which if you recall, we are extending in our class)
- logger.mode(message)
- Note:
- The logger can be used anywhere through out the application
- modes - OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL
- The logger is primarily used for debugging and in general, logs information, based on its mode, to the Bamboo system
- i.e. logger.info("Warning") would display the message "Warning" when the DebugLevel in the configuration of the file is set to the INFO mode
- As SandStorm runs all the stages intended for use in the application:
- It runs the init(ConfigDataIF config) function for each stage
- And collects the settings for the stage that are located in the configuration file
- This is done by using the config variable in the actual parameters of the init() function:
- i.e. if we have the following global variables in our configuration file:
- ServerName localhost
- ServerPort 10000
- We'd use:
- config.getString("ServerName") to pull the value, localhost
- config.getInt("ServerPort") to pull the value, 10000
- handleEvent(QueueElementIF event)
- Recall:
- handleEvent() is called whenever an event appears in our node
- "Specifically, an event is any Java object that descends from the QueueElementIF interface, and a stage is any Java object that descends from the EventHandlerIF interface." (http://bamboo-dht.org/programmers-guide.html)
- To be able to use Bamboo's P2P system and pass RPC's between stages, we must register a variable with Bamboo's router relevant to the class that is currently being created
- i.e. If we are working on Ping.java we'd define our application id as:
- long rpc_app_id = bamboo.router.Router.app_id(Ping.class)
- Server (Replier):
- With the RPC's mapped to their identifiers in the XDR File (See above section on XDR) we must now provide these identifiers to the stage responsible for it. To do this:
- We create a variable of type RpcRegisterReq (RPC request) that will encapsulate all events that the stage is responsible for:
- RpcRegisterReq rpc_register_req = new RpcRegisterReq(...);
- And fill it with:
- A mapping of all the event's procedure numbers (recall the triple RPC identifier) to their arguments and result methods defined in the XDR File
- i.e. Pseudo-code:
- Null Procedure Call
- (ping_api, ping_api_version, ping_null) => (void, void)
- Ping Procedure Call
- (ping_api, ping_api_version, ping) => (ping_args, ping_result)
- The RPC Application Id (rpc_app_id, set in the previous step)
- And some booleans
- Assign a callback method to notify the application when the RPCRegisterRequest has been completed
- rpc_register_request.cb = method();
- Set up the handler type for the RPCRegisterRequest
- rpc_register_request.handlers = new HashMap<>>();
- Map the handler for the RPCRegisterRequest to be one method, handle_rpc_call, which will forward all events by procedure number, to the method responsible for the event
- for (ProcKey key rpc_register_req.procedures.keySet()) rpc_register_req.handlers.put(key, handle_rpc_call);
- Note:
- The method handle_rpc_call(RpcCall rpc_call), uses a switch statement to map the procedure number of the event from the XDR file to the actual method in the application responsible for the event (the event is passed in as the rpc_call variable that is filled in by the application when the event does arrive)
- i.e. For the Ping example:
- 1 int proc_num =
- 2 rpc_call.proc.getProcNum()
- 3 switch (proc_num)
- 4 {
- 5 case chat.chat_null_1:
- 6 handleNull(rpc_call); break;
7 case chat.chat_ping_1:
- 8 handlePing(rpc_call); break;
- 9 default:break;}
- Set the stage's event handler to enqueue the RPCRegisterRequest variable
- Recall:
- This variable holds all the events for the stage
- rpc_server_stage.handleEvent(rpc_register_request)
- handlePing(RpcCall rpc_call) example:
- Recall:
- In the switch statement above in handle_rpc_call(), we map the procedure number of the events to the actual methods that are responsible for resolving the request. For this example we'll look at a simple manner of handling the ping request event
- Using the rpc_call argument, we typecast the arguments of the rpc_call to whichever arguments from the XDR file we are interested in handling, in this case, we typecast it to type ping_args
- ping_args args = (ping_args) rpc_call.args;
- We then create a variable of type ping_result, again taken from the XDR File and set the status of it to success or failure, to provide the response/reply to the request event that arrived and set the message field of the result to be the message received in the rpc_call (in other words, since this example echoes the message the client sends with the ping request, we set it here to send back to the client)
- Recall:
- We establish an enumerator, PingStatus, of status messages in the XDR File
- ping_result result = new ping_result();
- result.status = PingStatus.PING_STATUS_OK;
- resultt.msg = args.msg;
- We then create a variable of type RpcReply, which will encapsulate the result created above based on the rpc_call being handled:
- RpcReply rpc_reply = new RpcReply(result, rpc_call);
- Once the result has been embedded in an RpcReply object we hand it off again to the rpc_server_stage to send back to the client
- rpc_server_stage.handleEvent(rpc_reply);
- Client (Requester):
- To be able to make requests, a scheduler of type RpcScheduler needs to be created to register and store the events that the server stage is offering (the scheduler is set to be the instance of the node id, an inherent variable assigned throughout the Bamboo system)
- RpcScheduler rpc_scheduler = RpcScheduler.getInstance(my_node_id);
- Once initialized, a map needs to actually set the events the server stage is offering to a callback method to use once the client stage finishes the registration
- Map ping_proc_map = PingServer.getPingProcedureMap();
- rpc_scheduler.registerRpcProcedures(ping_proc_map, registration_rpc_cb)
- Finally, within the registration_rpc_cb() method, an event is created, initialized and requested/sent from/to the server
- i.e. In the ping example we request a ping event form the server so we need to create a ping_args variable and fill in the according data set in the XDR file
- ping_args args = new ping_args();
- ping_args.msg = "Test";
- We then embed the request in an PriorityRpcCall object based on its destination address, procedure number, priority and requester id
- ProcKey key = new ProcKey(ping.PING_API, ping.PING_API_VERSION, ping.ping_1);
- PriorityRpcCall rpc_call = new PriorityRpcCall(server_addr, key, ping_args, PriorityRpcCall.PRIORITY_HIGHEST, rpc_app_id);
- Once embedded into a PriorityRpcCall we assign a callback method, ping_cb(), to notify the application when the request has been received and responded to by the server (replier)
- rpc_call.cb = ping_cb;
- This method, just like the previous callbacks takes the rpc_call in the actual parameters and gets the status of the request along with any other data, in this case the echoed message.
- This callback is used to notify the client that the request (ping the server and echo a message) has been replied to (the status of the request was successful and the message received was the original one sent):
- ping_args args = (ping_args) rpc_call.args;
- String msg = args.msg;
- ping_result reply_args = (ping_result) rpc_reply.reply;
- int status = reply_args.status;
- logger.info("Received ping reply: msg=" + msg + " status=" + status);
- Lastly, we send off the request:
- rpc_scheduler.send(rpc_call);
Last modified 2007/08/17.
|
|