In this and the following chapter several examples of the usage of the Mnesia_Session interface will be introduced. The examples will first be written in Erlang and than the same functionality will be shown in a foreign language.
The examples will contain some basic operations, create_table, write and
read records both via the mnesia_session
and the
mnesia_corba_session
interfaces.
A simple database has been created, with people as the subject. The person record containing other data types will be demonstrated in the rest of the examples. To begin with we define the data types that will be used in IDL.
// // A simple example on different structures used to access mnesia from // the outside // // If the tables and structures should be accessed through a corba // interface, they need to be defined in a IDL specification so that // the mnesia_session interface can access the types needed to use // corba type any. // module persons { enum Sex {male, female}; struct data { Sex sex; long age; long phone; }; struct person { string name; // Used as key data personData; // A reference to other data structure string married_to; // A relation sequence <string> children; // A list }; };
Observe that the struct person, when mapping from IDL to Erlang, is prefixed
with the module name followed by '_'. In this example, the
table name would be persons_person
. If this is not
wanted the struct definition should be placed on the top level,
i.e. above the module definition.
A simplified example of a client written in Erlang to access
Mnesia
via the session interface is shown below.
Compile the person data definitions with:
erlc +'{be, erl_genserv}' person.idl
Include the necessary files:
%% Include to get the mnesia types, used in create table -include_lib("mnesia_session/include/mnesia.hrl"). %% Include my own definitions -include("persons.hrl").
Find the connector and create a private session:
start_session(Node) -> ConnPid = rpc:call(Node, mnesia_session_lib, lookup_connector, []), %% Use the factory to create a session SessPid = mnesia_connector:connect(ConnPid), {ConnPid, SessPid}.
In this example, we used
mnesia_session_lib:lookup_connector/0
but it is also possible to
use erlang:whereis(mnesia_connector)
.
In Erlang, objects are represented as processes. To access the method
of the object, call the method where the first argument is the object
reference, as in mnesia_connector:connect(ConnPid)
.
All functions in the IDL specification have an additional first
argument which is the object reference. More information is given in the
IC
and Orber
documentation.
To create a table with the structure defined in the IDL definition, we use the function create_person_table.
Define the table properties and call the function:
create_person_table(ObjKey) -> %% Define the table properties Attrs = [atom_to_list(F) || F <- record_info(fields, persons_person)], TabDef = #mnesia_TableDef{type = bag, mode = read_write, ram_copies = [], disc_copies = [], disc_only_copies = [], index_list = [4], %% Index on married_to attributes = Attrs, %% OBSERVE that the record_name must be %% exactly the same as the name of the %% structure/record record_name = "persons_person"}, Res = mnesia_session:create_table(ObjKey, "persons", TabDef), case Res of {ok, ""} -> ok; Else-> io:format("ERROR: ~s~n", [Else]) end.
The example insert and a read operation looks like:
insert_person(SessionKey, Name, Sex, Age, Phone, Mt, Ch) when list(Name), atom(Sex), integer(Age), integer(Phone), list(Mt), list(Ch) -> Data = #persons_data{sex = Sex, age = Age, phone = Phone}, Person = #persons_person{name = Name, personData = Data, married_to = Mt, children = Ch}, {ok, ""} = mnesia_session:dirty_write(SessionKey, "persons", Person), Person.
get_person(SessionKey, Name) when list(Name) -> {ok, RObj, ""} = mnesia_session:dirty_read(SessionKey, "persons", Name), hd(RObj).
Compile the person data definitions:
erlc +'{be, java}' person.idl
Import the necessary files.
import com.ericsson.otp.ic.*;
Find the connector and create a private session:
public static mnesia._sessionStub start_session(String args[]) { String SNode = new String(args[0]); String PNode = new String(args[1]); String Cookie = new String(args[2]); mnesia._connectorStub mccRef = null; mnesia._sessionStub mcsRef = null; try { mccRef = new mnesia._connectorStub(SNode+"0",PNode,Cookie, "mnesia_connector"); com.ericsson.otp.ic.Pid mccPid = mccRef.connect(); mcsRef = new mnesia._sessionStub(SNode+"1",PNode,Cookie,mccPid); return mcsRef; } catch(Exception se) { System.out.println("Unexpected exception: " + se.toString()); se.printStackTrace(); return null; } }
To create a table with the structure defined in the IDL definition, we use the function create_person_table. Define the table properties and call the function.
public static void create_person_table(mnesia._sessionStub mcsRef) { try { String name = "persons"; mnesia.TableDef def = new mnesia.TableDef(); def.type = mnesia.SetOrBag.bag; def.mode = mnesia.AccessMode.read_write; def.ram_copies = new String[0]; def.disc_copies = new String[0]; def.disc_only_copies = new String[0]; int[] idxs = new int[1]; idxs[0] = 4; def.index_list = idxs; String[] attrs = new String[4]; attrs[0] = "name"; attrs[1] = "personData"; attrs[2] = "married_to"; attrs[3] = "children"; def.attributes = attrs; def.record_name = "persons_person"; // The used IDL type StringHolder reason; reason = new StringHolder(); if(mnesia.Status.ok != mcsRef.create_table(name, def, reason)) System.out.println("Create Table Error " + reason.value); } catch(Exception se) { System.out.println("Unexpected exception: " + se.toString()); return; } }
The example insert and a read operation looks like:
public static void insert_person(mnesia._sessionStub mcsRef, String name, persons.Sex sex, int age, int phone, String mt, java.lang.String[] children) { persons.data data; data = new persons.data(sex, age, phone); persons.person person = new persons.person(); person.name = name; person.personData = data; person.married_to = mt; person.children = children; try { StringHolder reason = new StringHolder(); Term object = new Term(); persons.personHelper.insert(object,person); if(mnesia.Status.ok != mcsRef.dirty_write("persons", object, reason)) System.out.println("Insert person Error " + reason.value); } catch(Exception se) { System.out.println("Unexpected exception: " + se.toString()); return; } }
public static persons.person get_person(mnesia._sessionStub mcsRef, String name) { try { StringHolder reason = new StringHolder(); Term key = new Term(); mnesia.RecordlistHolder res = new mnesia.RecordlistHolder(); key.insert_string(name); if(mnesia.Status.ok == mcsRef.dirty_read("persons", key, res, reason)) { if(res.value.length > 0) { persons.person rec1 = persons.personHelper.extract(res.value[0]); return rec1; } else return null; } else { System.out.println("Insert person Error " + reason.value); return null; } } catch(Exception se) { System.out.println("Unexpected exception: " + se.toString()); return null; } }
Note: the usage of ETERM
differs a little to the CORBA case, when using Java it mapps to the Term
class which inherits from Any
class.
Compile the person data definitions:
erlc +'{be, c_genserv}' person.idl
Include the necessary files.
#include <erl_interface.h> #include "mnesia_session.h" #include "mnesia_connector.h" #include "persons.h" #include <ic.h>
Find the connector and create a private session:
erlang_pid start_session(CORBA_Environment * env) { erlang_pid session_pid; session_pid = mnesia_connector_connect(NULL, env); if(env->_major != CORBA_NO_EXCEPTION) handle_error("connector_connect", env); return session_pid; }
To create a table with the structure defined in the IDL definition, we use the function create_person_table. Define the table properties and call the function.
void create_person_table(CORBA_Environment * env) { int err; char * reason; mnesia_Status result; mnesia_TableDef tabdef; mnesia_Indices idxs; long idx_list[1] = {4}; char * attrs[4] = {"name", "personData", "married_to", "children"}; tabdef.type = mnesia_bag; tabdef.mode = mnesia_read_write; tabdef.ram_copies._maximum = 0; tabdef.ram_copies._length = 0; tabdef.ram_copies._buffer = NULL; tabdef.disc_copies._maximum = 0; tabdef.disc_copies._length = 0; tabdef.disc_copies._buffer = NULL; tabdef.disc_only_copies._maximum = 0; tabdef.disc_only_copies._length = 0; tabdef.disc_only_copies._buffer = NULL; tabdef.index_list._maximum = 5; tabdef.index_list._length = 1; tabdef.index_list._buffer = idx_list; tabdef.attributes._maximum = 5; tabdef.attributes._length = 4; tabdef.attributes._buffer = attrs; tabdef.record_name = "persons_person"; /* The name of the stored type/struct */ result = mnesia_session_create_table(NULL, "persons", &tabdef, &reason, env); if(env->_major != CORBA_NO_EXCEPTION) { fprintf(stderr,"\n error in create_table: %d\n", env->_major); exit(1); } else if(result != mnesia_ok) { fprintf(stderr, "Create table failed with reason %s", reason); } CORBA_free(reason); }
The example insert and a read operation looks like:
void insert_person(CORBA_Environment * env, char *name, persons_Sex sex, int age, int phone, char * mt, persons_person_children * ch) { ETERM *person, *children, *temp; int err, i; char * reason; mnesia_Status result; extern char * oe_persons_Sex[]; children = erl_mk_empty_list(); if(ch != NULL) for(i = 0; i < ch->_length; i++) { temp = erl_mk_string(ch->_buffer[i]); children = erl_cons(temp, children); erl_free_term(temp); }; person = erl_format("{persons_person,~s,{persons_data,~a,~i,~i}, ~s, ~w}", name, oe_persons_Sex[sex], age, phone, mt, children); result = mnesia_session_dirty_write(NULL, "persons", person, &reason, env); if(env->_major != CORBA_NO_EXCEPTION) { fprintf(stderr,"\n error in insert_person: %d\n", env->_major); exit(1); } else if(result != mnesia_ok) { fprintf(stderr, "Insert person failed with reason %s", reason); exit(-1); } erl_free_term(children); erl_free_term(person); CORBA_free(reason); }
void get_person(CORBA_Environment * env, char * name, persons_person * person) { int err, i; char * reason; mnesia_Status result; mnesia_Recordlist *rec_list; ETERM * key; key = erl_mk_string(name); result = mnesia_session_dirty_read(NULL, "persons", key, &rec_list, &reason, env); if(env->_major != CORBA_NO_EXCEPTION) { fprintf(stderr,"\n error in get_person: %d\n", env->_major); exit(1); } else if(result != mnesia_ok) { fprintf(stderr, "Get person failed with reason %s", reason); exit(-1); } if(rec_list->_length > 0) { /* Only interrested in the first match */ decode_person(rec_list->_buffer[0], person); for(i=0; i < rec_list->_length; i++) erl_free_term(rec_list->_buffer[i]); } else { fprintf(stderr, "Insert person failed empty_list returned "); exit(-1); } CORBA_free(rec_list); CORBA_free(reason); erl_free_term(key); }
Note: the usage of ETERM
differs when using C as the user must develop their own code to create and extract information from the ETERM
structures, whereas, CORBA does this automatically.