Some tips on REBOL/Services

How does one use REBOL/Services (also known as R/S, LNS, RebServices)? The docs are not yet finished, as R/S is not finished, so it can be hard to judge for yourself how well it works.

This post will try to explain in a quick fashion how to get R/S working.

Rugby vs. REBOL/Services

Let me tell you that R/S is used throughout DevBase and it works very well. Using R/S requires a bit more code (particularly in the connectivity area) than Rugby does and is therefore not nearly as easy to get running in 2 minutes, but it offers many more high level features like user management, roles and authentication.

R/S forces a service structure on you, so you can't directly compare the code structure to Rugby, which is more free form, in that you "publish" a function for network usage. R/S is inherently more secure, as everything is transmitted in dialect form, making it simple to produce a secure service that does not allow access to any sensitive information behind it.

On the low-level side, R/S always uses REBOL's built-in encryption, which scales with the REBOL product used. There is currently no encryption enabled by default in Rugby, as I've found that REBOL becomes unstable and will crash with that enabled.

R/S will continue development when R3 gets to be more mature while Rugby development has stagnated for a long time. There is currently no sign that it will pick up again. Therefore it might be better to stick to R/S in the long run. I've not tested whether performance is better or worse than Rugby, but I would expect REBOL/Services to be a tad slower than Rugby, due to built in encryption and authentication. With R3, it's also expected to become a built-in part of the standard R3 distribution, and it's very likely to become truly and fully asynchronous, outstripping Rugby's co-operatively threaded speed advantage. R/S does however not contain an asynchronous cron scheduler, that allows you to run jobs in the background of your REBOL process. This might be handy still in R2, but will be irrelevant in R3 due to the existence of tasks.

Using the DevBase source code alone, I managed to put together a primitive source and software management system, that I'm building for my own use. The client part uploads source code to the server and through R/S commands, initiates on-the-fly building and delivery of finished software to end-users. Since DevBase is not public yet, the source code can't be published yet, but I can certainly publish some of the sources for my own server/client system. I'm not going to post the whole thing, because I don't want it to be used anywhere yet as it's very far from finished and it also might be too confusing to read as it is a prototype. The design is not very good.

It does in no way list all its features and caveats. At this time there are caveats and incomplete parts in R/S. R/S is also only working on R2 at this time, due to R3's radically different port design.

How to load the client code

R/S consists of many source files that can all be loaded using a single file, called load-client.r. Once that is loaded, you are able to start connecting to a server. I've not optimized exactly what source files are needed, so there might be some parts loaded that are not necessary.

You should be able to load the client at any time in your code.

How to load the server code

This is the same as for the client code, except that you use load-server.r instead. Once the server code is ready, you can begin loading services and start serving them.
When the server starts, it prints out a bunch of debugging information during startup, among which services has started successfully and which have not started. Note that R/S will continue to run even if your service has failed to load or even if the service you are trying to load does not exist.

Here's the source code for my own server:
; each network connect should not be printed on the console
system/options/quiet: true

service-port: tcp://:8000

do %rebol-services/load-server.r

server-config/service-dir: what-dir

;ctx-services/debug-enable: false ; uncomment to turn off debug information

service: start-service/options service-port [
services: [
%service-application.r
%service-articles.r
%service-accounting.r
]
]

wait []

How to preprocess R/S into your program

If you need to encap a program that uses R/S, I wrote a preprocessor file that includes the files used by load-client.r in the same order. I did not make a file that corresponds to the server part, as I've not yet encapped a server, but you should be able to do that using the same method with the load-server.r file.

Code:
REBOL [Title: "Load Services for Client Usage"]

ctx-services: [ ]

; must append each file to ctx-services

append ctx-services load [#include %services/define.r]
append ctx-services load [#include %services/log.r]
append ctx-services load [#include %services/session.r]
append ctx-services load [#include %services/packet.r]
append ctx-services load [#include %services/crypt.r]
append ctx-services load [#include %services/pass.r]
append ctx-services load [#include %services/queue.r]
append ctx-services load [#include %services/client-config.r]
append ctx-services load [#include %services/client-tcp.r]
append ctx-services load [#include %services/client-http.r]
append ctx-services load [#include %services/client-api.r]

ctx-services: context ctx-services

foreach word [
open?
session-service?
add-client-handler
open-service close-service send-service on-service
query-service wait-service abort-service do-service
login-service logout-service
][
set word get in ctx-services word
]

How to query the server

The server consists of various services that can be queried. There are pretty good details about how to do that in the official tutorial docs here. R/S comes with a few services as standard, such as file transfer and a time service. If you launch R/S without any of your own services, you can therefore still test it.
A good way to know that your R/S server and client are connecting and working together, is to load your server in one console:
>> do %load-server.r

start it:
>> service: start-service tcp://:8000
>> wait []

and load the client in another console:
>> do %load-client.r

and submitting this from the client console:
>> do-service tcp://server:8000 [time]

If you get a time as a result back, the connection is working:
== [
done [reply seq 1 service "Generic Service" commands 1 time 0:00:01]
ok [time 18-Mar-2008/12:08:12+1:00]
]

If there is no access, make sure that you can communicate via port 8000. You can use any port you want, however if you are using MacOSX, Linux or other Unix style OS, you must have administrator or root privileges to use ports below 1024.

How to process the result

DevBase uses PARSE to parse the results into what you need. Using PARSE allows you to mirror the commands asked into result sets with the same structure. There are of course other methods to handle result blocks, but I found this part to be easiest and most flexible to do with PARSE.
For my own client code, I created a function with a single result parser. This way I can use the same function for any commands sent to the server.
DevBase uses a separate parser for each separate case. I assume you know how to use PARSE, so I won't waste time with examples. Happy Also there are plenty of information in the manual on what returned data looks like.

How to create a service

There are multiple methods to create a service, but I'll only explain here how to do it with a set of files. Each service is created as a single file that is loaded by R/S. It has a specific header that tells the title and purpose of the service. The service files are usually stored in the %services/ directory in relation to where your R/S code is stored.

Here is the code for one real-life working service, called APPLICATION. There are references to some contexts in there, that are not listed in the example, so don't worry about those. The comments are for clarity and are not necessary:
; ---------- Header Part

name: 'application
title: "Softserve Application Service"
description: "Builds and serves applications from the Softserve repository."

; ---------- Service Commands

commands: [
'set arg: object! "Sets a value on the server" (
app/current-program: make app/current-program arg/1
program?: result: app/load-repository-info
result: any [object? result 'no-program]
)
| 'make "Makes a new program in the repository" (
result: either program? [
'program-exists
][
any [object? app/make-repository-info 'no-program]
]
)
| 'compare "Compares the current program with the latest version" (
result: program-set? [app/compare]
)
| 'get arg: ['stable | 'unstable | tuple!]
"Gets a value or a program from the server" (
result: program-set? [app/dispatch arg/1]
)
| 'store arg: [block! | binary!] "Stores the current package" (
result: program-set? [app/store-package arg/1]
)
| 'stats "Application Service Statistics" (result: stats)
| 'current "Returns the current program" (
result: program-set? [app/current-program]
)
| 'info "Returns the info object for the current program" (
result: program-set? [app/repository-info]
)
]

The COMMANDS block should have a structure similar to this:

[
'command "Help Text" (result: action-code)
| 'command-2 "Help Text 2" (result: action-code-2)
| etc...
]

The action should store a value in the RESULT word. This result is what the client receives.

I don't know whether other structures will work. If the structure or the file is incorrect in some way, the service is not loaded. R/S will notify you of that in the debug output, but it will continue to start other services that work, so be sure to notice whether it loads correctly.

Conclusion

If you get all this: 1. a loading server, 2. a loading client, 3. a loading service on the server, and 4. you can connect with the test line as mentioned above, you should be able to use R/S.

In that case, congratulations! If not... well... Happy
|