Some tips on REBOL/Services
18-Mar-2008 11:05 Filed in: 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.
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.
You should be able to load the client at any time in your code.
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:
Code:
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:
start it:
and load the client in another console:
and submitting this from the client console:
If you get a time as a result back, the connection is working:
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.
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.
Also there are plenty of
information in the manual on what returned data looks like.
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:
The COMMANDS block should have a structure similar to this:
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.
In that case, congratulations! If not... well...
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, calledload-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 useload-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 byload-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.
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...
|