I have an upcoming project that will require a custom LDAP server backed by an existing inventory/asset management application, which runs on Oracle. Of course, I plan to tackle this in OCaml. I’m confident enough with Oracaml now, and I’m delighted to find that OCaml’s LDAP module includes a server API. So to construct a new LDAP server, the process seems to be very straightforward:
- Implement a function
(Ldap_funserver.connection_id → Ldap_types.ldap_message → unit → Ldap_types.ldap_message)
to actually do the lookup and return the result. This is the only functionality I require at this stage – the underlying data will be managed by the existing application, so no need for add, modify, delete and so on. I don’t even need authentication since it will run on trusted network. This function wll get the request in anldap_message
and construct anldap_message
with the reply. - Create a record of type
backendInfo
populatingbi_op_search
with my new function. All fields of this type are optional. - Pass this record to
init
, and get back a record of typeserver_info
. - Pass that to
run
and we’re ready to start accepting clients. Perhaps I’ll put in a signal handler for a cleanshutdown
too.
Here’s where it gets more fiddly. I can also implement bi_init (unit → unit)
which is called as the server starts up (and there is a corresponding bi_close
called when shutting down). These are the logical places to place occi_connect
and occi_disconnect
respectively, and occi_create_statement
in the function in step (1) with the lda
created therein. What I don’t understand is how to get that from there and into my handler function, without using global mutable state.
I am starting to think that as and when I start implementing my own APIs I will always include an arbitary type in the method signature, that the developer can just set to () if they don’t care, a sort of poor man’s Reader monad…
Perhaps the way to do this is to skip
bi_init
, connect to Oracle first and then givebi_op_search
a curried function, which has already been applied to the connection… And give a similar function tobi_close
.I don’t think the mutable state need be global; it is sufficient that the closure you pass as the bi_init function closes over it. I.e.:
You can keep that scope small by calling out to functions from a higher-up scope, passing the state explicitly. That seems decidedly less evil.
Good point – I should think a bit about the subtle difference between a closure and a curried function.
Pingback: LDAP (2) | So I decided to take my work back underground