# recital < mrprog.prgIndividual commands can be executed in shell scripts.
# recital < myprog.prg > myoutput.txt
# recital > myoutput.txt <<END
use customers
list structure
END
# echo "select * from sales!customers where overdue" | recital | wc -l
# recital -c "create database sales"Expressions can be evaluated and used in shell scripts.
# recital -c "create table sales!invoices (id int, name char(25), due date)"
# VER=`recital -e "version(1)"`You can view what command line options are available by typing:
# recital --help
For systems that do not have the xterm libraries installed, please install these to use xterm, or set the DB_TERM environment variable to start Recital from a terminal:
DB_TERM=gnome-terminal; export DB_TERM
This setting can be added to the /opt/recital/conf/recital.conf (text) file to make it available system-wide.
Please note that the Recital ODBC Driver for Linux requires a 32 bit ODBC Driver Manager.
Centos 6:
sudo yum install zlib-devel.i686 pam-devel.i686(and accept dependencies)
Then run the installer in text mode
sudo ./recital-10.0.3-linux32.bin --mode textRun Recital with sudo the first time, to set the system filetype compatiblity settings.
sudo recitalAfter saving the compatibility settings, quit to exit, then run Recital as your preferred user.
> quit
$ recital
RedHat / Fedora family:
sudo yum install zlib-devel.i686 pam.i686(and accept dependencies)
Then run the installer in text mode
sudo ./recital-10.0.3-linux32.bin --mode textRun Recital with sudo the first time, to set the system filetype compatiblity settings.
sudo recitalAfter saving the compatibility settings, quit to exit, then run Recital as your preferred user.
> quit
$ recital
Ubuntu family:
sudo apt-get install ia32-libsIn later versions of Ubuntu, ia32-libs is obsolete. The following package should be installed:
sudo apt-get install lib32z1Ubuntu 12.04 and above also require the following:
sudo apt-get install libpam0g:i386Then run the installer in text mode
sudo ./recital-10.0.3-linux32.bin --mode textRun Recital with sudo the first time, to set the system filetype compatiblity settings.
sudo recitalAfter saving the compatibility settings, quit to exit, then run Recital as your preferred user.
> quit
$ recital
In this article Yvonne Milne looks at the use of the Recital Remote Data Connectivity Functions with Recital Database Gateways.
All temporary files created by Recital are stored in the directory specified by the environment variable DB_TMPDIR.
mkdir /opt/recital/tmp
mount -t tmpfs -o size=1g recitaltmpfs /usr/recital/tmp
When stress testing our loadbalancer, i was unable to get more than 20 reliable ssh connections. The following error would be reported.
ssh_exchange_identification: Connection closed by remote host
The resolution for this is quite simple.
Edit the /etc/ssh/sshd_config file and increase the MaxStartups. On my server i set this to 200.
Edit /etc/sysctrl.conf and add the following line:
net.core.netdev_max_backlog = 3000
Then apply this change:
# sysctl -p
In this article Barry Mavin, CEO and Chief Software Architect for Recital details how to Build C Extension Libraries to use with Recital.
Overview
It is possible to extend the functionaliy of Recital products using "Extension libraries" that can be written in C. These extension libraries, written using the Recital/SDK API, are dynamically loadable from all Recital 9 products. This includes:
- Recital
- Recital Server
- Recital Web
Building C Extension Libraries
You can create C wrappers for virtually any native operating system function and access these from the Recital 4GL. Unlike traditional APIs which only handle the development of C functions that are callable from the 4GL, the Recital/SDK allows you to build Classes that are accessible from all Recital products. e.g. You could create a GUI framework for Linux that handles VFP system classes!
To deploy your C Extension Libraries, copy them to the following location:
Windows:
\Program Files\Recital\extensions
Linux/Unix:
/opt/recital/extensions
Please see the Recital/SDK API Reference documentation for further details.
Sample code
Listed below is the complete example of a C Extension Library.:
////////////////////////////////////////////////////////////////////////////////
#include "mirage_demo.h"
////////////////////////////////////////////////////////////////////////////////
// Declare your functions and classes below as follows:
//
// Recital Function Name, C Function Name, Type (Function or Class)
//
#define MAX_ELEMENTS 7
static struct API_SHARED_FUNCTION_TABLE api_function_table[MAX_ELEMENTS] = {
{"schar", "fnSamplesCharacter", API_FUNCTION},
{"stype", "fnSamplesType", API_FUNCTION},
{"slog", "fnSamplesLogical", API_FUNCTION},
{"snum", "fnSamplesNumeric", API_FUNCTION},
{"sopen", "fnSamplesOpen", API_FUNCTION},
{"myclass", "clsMyClass", API_CLASS},
{NULL, NULL, -1}
};
////////////////////////////////////////////////////////////////////////////////
// Recital API initialization. This should be in only ONE of your C files
// **IT SHOULD NEVER BE EDITED OR REMOVED**
INIT_API;
///////////////////////////////////////////////////////////////////////
// This is an example of passing a character parameter and returning one.
RECITAL_FUNCTION fnSamplesCharacter(void)
{
char *arg1;
if (!_parse_parameters(PCOUNT, "C", &arg1)) {
ERROR(-1, "Incorrect parameters");
}
_retc(arg1);
}
///////////////////////////////////////////////////////////////////////
// This is an example of passing a numeric parameter and returning one.
RECITAL_FUNCTION fnSamplesNumeric(void)
{
int arg1;
if (!_parse_parameters(PCOUNT, "N", &arg1)) {
ERROR(-1, "Incorrect parameters");
}
_retni(arg1);
}
///////////////////////////////////////////////////////////////////////
// This is an example returns the data type of the parameter passed.
RECITAL_FUNCTION fnSamplesType(void)
{
char result[10];
if (PCOUNT != 1) {
ERROR(-1, "Incorrect parameters");
}
switch (_parinfo(1)) {
case API_CTYPE:
strcpy(result, "Character");
break;
case API_NTYPE:
strcpy(result, "Numeric");
break;
case API_LTYPE:
strcpy(result, "Logical");
break;
case API_DTYPE:
strcpy(result, "Date");
break;
case API_TTYPE:
strcpy(result, "DateTime");
break;
case API_YTYPE:
strcpy(result, "Currency");
break;
case API_ATYPE:
strcpy(result, "Array");
break;
default:
strcpy(result, "Unkown");
break;
}
_retc(result);
}
///////////////////////////////////////////////////////////////////////
// This is an example returns "True" or False.
RECITAL_FUNCTION fnSamplesLogical(void)
{
char result[10];
int arg1;
if (!_parse_parameters(PCOUNT, "L", &arg1)) {
ERROR(-1, "Incorrect parameters");
}
if (arg1) strcpy(result, "True");
else strcpy(result, "False");
_retc(result);
}
///////////////////////////////////////////////////////////////////////
// This example opens a table.
RECITAL_FUNCTION fnSamplesOpen(void)
{
char *arg1;
if (!_parse_parameters(PCOUNT, "C", &arg1)) {
ERROR(-1, "Incorrect parameters");
}
if (_parinfo(1) == API_CTYPE) {
_retni(COMMAND(arg1));
} else {
_retni(-1);
}
}
///////////////////////////////////////////////////////////////////////
// Define the MyClass CLASS using the API macros
///////////////////////////////////////////////////////////////////////
RECITAL_EXPORT int DEFINE_CLASS(clsMyClass)
{
/*-------------------------------------*/
/* Dispatch factory methods and return */
/*-------------------------------------*/
DISPATCH_FACTORY();
/*---------------------------------*/
/* Dispatch constructor and return */
/*---------------------------------*/
DISPATCH_METHOD(clsMyClass, Constructor);
/*--------------------------------*/
/* Dispatch destructor and return */
/*--------------------------------*/
DISPATCH_METHOD(clsMyClass, Destructor);
/*-----------------------------------*/
/* Dispatch DEFINE method and return */
/*-----------------------------------*/
DISPATCH_METHOD(clsMyClass, Define);
/*------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property NumValue */
/* then return. */
/*------------------------------*/
DISPATCH_PROPSET(clsMyClass, NumValue);
DISPATCH_PROPGET(clsMyClass, NumValue);
/*------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property LogValue */
/* then return. */
/*------------------------------*/
DISPATCH_PROPSET(clsMyClass, LogValue);
DISPATCH_PROPGET(clsMyClass, LogValue);
/*-------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property DateValue */
/* then return. */
/*-------------------------------*/
DISPATCH_PROPSET(clsMyClass, DateValue);
DISPATCH_PROPGET(clsMyClass, DateValue);
/*-------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property TimeValue */
/* then return. */
/*-------------------------------*/
DISPATCH_PROPSET(clsMyClass, TimeValue);
DISPATCH_PROPGET(clsMyClass, TimeValue);
/*-------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property CurrValue */
/* then return. */
/*-------------------------------*/
DISPATCH_PROPSET(clsMyClass, CurrValue);
DISPATCH_PROPGET(clsMyClass, CurrValue);
/*-------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property CharValue */
/* then return. */
/*-------------------------------*/
DISPATCH_PROPSET(clsMyClass, CharValue);
DISPATCH_PROPGET(clsMyClass, CharValue);
/*------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property ObjValue */
/* then return. */
/*------------------------------*/
DISPATCH_PROPSET(clsMyClass, ObjValue);
DISPATCH_PROPGET(clsMyClass, ObjValue);
/*-----------------------------------*/
/* If message not found return error */
/*-----------------------------------*/
OBJECT_RETERROR("Unknown message type");
}
////////////////////////////////////////////////////////////////////////////////
// Define METHOD handlers
////////////////////////////////////////////////////////////////////////////////
DEFINE_METHOD(clsMyClass, Constructor)
{
struct example_data *objectDataArea;
/* Allocate memory for objects objectData area */
objectDataArea = (struct example_data *)
malloc(sizeof(struct example_data));
if (objectDataArea == NULL) return(-1);
/* Assign the default property values */
strcpy(objectDataArea->prop_charvalue, "Test API object");
objectDataArea->prop_numvalue = 15.2827;
objectDataArea->prop_logvalue = 'F';
strcpy(objectDataArea->prop_datevalue, DATE_DATE());
strcpy(objectDataArea->prop_timevalue, DATE_DATETIME());
strcpy(objectDataArea->prop_currvalue, "15.2827");
strcpy(objectDataArea->object_name, "APIobject");
objectDataArea->prop_objvalue
= OBJECT_NEW(objectDataArea->object_name, "exception", NULL);
/* Set the object objectData area */
OBJECT_SETDATA((char *)objectDataArea);
return(0);
}
DEFINE_METHOD(clsMyClass, Destructor)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData != NULL) {
if (objectData->prop_objvalue != NULL)
OBJECT_DELETE(objectData->prop_objvalue);
free(objectData);
objectData = NULL;
}
return(0);
}
DEFINE_METHOD(clsMyClass, Define)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
char buffer[512];
int rc;
/* Check the object class */
OBJECT_GETPROPERTY(objectData->prop_objvalue, "class", buffer);
rc = OBJECT_GETARG(buffer, &result);
if (result.errno == 0 && result.type == 'C'
&& strcmp(result.character, "Exception") == 0) {
switch (OBJECT_GETARGC()) {
case 1:
rc = OBJECT_GETPARAMETER(1, &result);
if (result.errno == 0 && result.type == 'C') {
OBJECT_SETARG(buffer, &result);
rc = OBJECT_SETPROPERTY(objectData->prop_objvalue,
"message", buffer);
}
break;
case 2:
rc = OBJECT_GETPARAMETER(2, &result);
if (result.errno == 0 && result.type == 'N') {
OBJECT_SETARG(buffer, &result);
rc = OBJECT_SETPROPERTY(objectData->prop_objvalue,
"errorno", buffer);
}
}
}
result.type = 'L';
result.logical = (rc == 0 ? 'T' : 'F');
OBJECT_RETRESULT(&result);
}
////////////////////////////////////////////////////////////////////////////////
// Define GET property handlers
////////////////////////////////////////////////////////////////////////////////
DEFINE_PROPERTYGET(clsMyClass, NumValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('N', objectData->prop_numvalue);
}
DEFINE_PROPERTYGET(clsMyClass, LogValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('L', objectData->prop_logvalue);
}
DEFINE_PROPERTYGET(clsMyClass, DateValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('D', objectData->prop_datevalue);
}
DEFINE_PROPERTYGET(clsMyClass, TimeValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('T', objectData->prop_timevalue);
}
DEFINE_PROPERTYGET(clsMyClass, CurrValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('Y', objectData->prop_currvalue);
}
DEFINE_PROPERTYGET(clsMyClass, CharValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('C', objectData->prop_charvalue);
}
DEFINE_PROPERTYGET(clsMyClass, ObjValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('O', objectData->prop_objvalue);
}
////////////////////////////////////////////////////////////////////////////////
// Define SET property handlers
////////////////////////////////////////////////////////////////////////////////
DEFINE_PROPERTYSET(clsMyClass, NumValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'N') {
objectData->prop_numvalue = result.number;
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, LogValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'L') {
objectData->prop_logvalue = result.logical;
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, DateValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'D') {
strcpy(objectData->prop_datevalue, DATE_DTOS(result.date));
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, TimeValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'T') {
strcpy(objectData->prop_timevalue, DATE_TTOS(result.datetime));
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, CurrValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'Y') {
strcpy(objectData->prop_currvalue, CURR_YTOS(result.currency));
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, CharValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'C') {
strcpy(objectData->prop_currvalue, result.character);
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, ObjValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
OBJECT objvalue;
int rc = OBJECT_ERROR;
if (OBJECT_GETTYPE() == 'O') {
objvalue = OBJECT_GETOBJECT();
objectData->prop_objvalue = OBJECT_ASSIGN(objvalue, objectData->object_name);
rc = OBJECT_SUCCESS;
}
return(rc);
}
In Adobe's own words:"Adobe® AIR® is a cross-operating system runtime that lets developers combine HTML, Ajax, Adobe Flash®, and Adobe Flex® technologies to deploy rich Internet applications (RIAs) on the desktop."
The outcome of this combination of technologies is that developers can design and render quite beautiful user interfaces cross platform. For us desktop Linux users it is nice to have
an additional avenue for obtaining and running attractive desktop applications.Examples of great Adobe air applications are Adobe.com for My Desktop, TweetDeck and the Times Reader. You can download these applications and many more at the Adobe Marketplace.
The easiest way to install Adobe Air on Fedora 12 is to download the latest build from Adobe, click here.
Once you have downloaded the .bin file do the following at the shell:
su - chmod +x AdobeAIRInstaller.bin ./AdobeAIRInstaller.binOnce you have Air installed, there is a slight tweak you will have to do to get it running on Fedora 12, it is related to the security certificates. This can be remedied in one simple line at the shell prompt as root.
su - for c in /etc/opt/Adobe/certificates/crypt/*.0; do aucm -n $(basename $c) -t true; doneWhat this line is doing is using the aucm which is the Adobe Unix certificate manager to set the certificates installed as trusted.
You will now be able to go to the Adobe Marketplace and download and run Air applications without any issues.
Enjoy!
In this article Barry Mavin explains step by step how to setup a Linux HA (High Availability) cluster for the running of Recital applications on Redhat/Centos 5.3 although the general configuration should work for other linux versions with a few minor changes.