Yoix / Contents of the Top-Level YWAIT README File
YWAIT Version 2.0
This software is covered by the Common Public License Version 1.0.
YWAIT (Yoix Web Application Instant Template) provides a substantial
and expandable framework for the server side of a Web based application
that uses Perl for most of the server side scripts and relies on Java
and the Yoix interpreter to provide the client side GUI. We've used
variations of this architecture, which we described in the 2002 paper
http://www.yoix.org/papers/lncs23760090.pdf
to build large web based production systems and we've done it for many
years now. Even though we've made many changes in this version you can
still expect reliable, flexible, production quality behavior from the
YWAIT client and server software.
-------------
Yoix Software
-------------
This version of YWAIT uses features that were introduced in the 2.0.0
Yoix release, which means it won't work with an old Yoix interpreter.
For completeness, we include a compatible version of the interpreter
in the binary file
jars/yoix.jar
so this really is a standalone package that you don't have to update.
However, if you visit the official Yoix web site
http://www.research.att.com/sw/tools/yoix
or
http://www.yoix.org/
you can always download the source for the Yoix interpreter and build
your own yoix.jar file. Documentation is also available at that web
site, so it's the place to go if you have questions about Yoix.
------------
What You Get
------------
The source package includes everything you need to build and install
the server software on systems that are equipped with standard Unix
and Java development tools and run a web server, like Apache. YWAIT
also builds an installer for the client-side software, and the HTML
document that's displayed when you point a browser at your web site
contains a link to that installer. The client software is the Yoix
interpreter (perhaps augmented by custom modules) in an executable
jar file that starts the interpreter using a very small application
specific class that knows how to get to your web site. Clients run
the installer once and then rely on the server to keep everything,
including the jar file installed on their system, up to date.
We've used variations of this architecture since 1998 and we think
of it as a "Web Application", but you may prefer to call it a "Java
Application", "Rich Internet Application" or something else that may
not be fit to print - the choice is yours, but only after you really
try the software. There's no browser or JavaScript involved in our
architecture and we don't rely on HTML or XML. Instead, Java takes
the place of the browser and we use the Yoix interpreter, which is
written in Java, for scripting and providing a platform independent
interface to Java. The format of the data exchanged between a client
and the server is not specified. Instead, the Yoix script that builds
the screen used to present the data and the cgi script that delivers
data to that screen make the decision, and they can pick any format
they want, including HTML or XML.
This architecture and our desire to use it and Java to deliver real
production applications inside AT&T is why Yoix was written and why
platform independence was and still is a primary focus of the Yoix
interpreter. A single (rather large) Java application, namely the
Yoix interpreter, concentrates really hard on tricky Java issues
and also happens to accept a reasonably simple scripting language.
Stick the interpreter between the Java Virtual Machine running on
a client and Yoix scripts sitting on a server and there's a pretty
good chance you'll end up with a portable application. But there's
an advantage even when you don't - one Java program means one place
to address portability issues, and when it's done right Yoix scripts
everywhere benefit.
So it's the Yoix scripts sitting on the server that are endowed with
the "Write Once, Run Anywhere" property and it's the Yoix interpreter
that we change when portability issues arise. The interpreter lets us
reuse our Java code while the language, which intentionally omits a way
to access arbitrary Java features, limits Yoix scripts to the features
that are exposed by the Yoix interpreter. An obvious downside is that
the Yoix interpreter always lags Java, but the same thing undoubtedly
can be said about any substantial Java application. Java's latest and
greatest features usually take time to appear in production programs,
and that's really the way it should be.
----------
You Decide
----------
This is a solid package that delivers production quality performance
and it's a good example of the Yoix technology that we developed many
years ago and continue to use today. There's much more we could say,
but you shouldn't believe our sales pitch. Instead build, install and
test the software and you can make up your own mind about performance
and portability. Devote some time to understanding the server software
and the Yoix language and you'll be in a position to comment on the
flexibility of the YWAIT approach. Try https if you're concerned about
security and you can decide if you're satisfied with the behavior.
-----------------
Your Requirements
-----------------
Building and installing your application doesn't require any unusual
expertise. Being comfortable talking to a shell and a text editor on
some flavor of Unix (e.g., Linux, Solaris, OSX) should be about all
the experience you need to configure, build, install, and test your
system. You'll have to poke around a bit on your system, mostly for
information about your web server, update some variable definitions
that our makefiles use, run make to install your application, point
a browser at your web site to make sure your web server is behaving,
and use java when you're really ready to test your application.
You need standard Unix and Java development tools (e.g., make, java,
javac, sed, cc, sh, perl etc.) to build and install your application.
Most of our makefiles are simple, but even the complicated ones avoid
using features that would have required a particular version of make.
The story with java and javac isn't nearly as forgiving, so you won't
get far without a relatively new version of the Java Development Kit.
We prefer 1.5.0 (Sun may call it 5.0), but 1.4.0 or later should let
you complete the build. If you plan on running or testing the client
from your server then you might as well install 1.5.0.
Any version of Perl5 or later should work, but you will need to make
sure the following perl packages are available:
Config URI::Escape Compress::Zlib IO::File
You can easily check by running a script that contains the lines:
require Config;
require URI::Escape;
require Compress::Zlib;
require IO::File;
If the script completes without complaining, you are OK. If you need
to install packages, they do not have to be in the system perl package
area, they can be installed elsewhere, but you will need to set the
PERL_LIB_LINE entry in the gsubsti._YX file that is located in the
src/scripts directory. Perl packages can be found at the
http://www.cpan.org
site.
---------------
Our Assumptions
---------------
Our main test and development system was a Linux PC running an Apache
web server. Root access wasn't an issue and there were no applications
or users to worry about, so we could change the web server's behavior
and nobody complained. It's not the required environment and probably
doesn't describe your server, but it's the environment we'll assume in
this file. Your job, as you read these instructions, is to translate
them back to your web server or somehow convey the information to your
friendly system administrator.
As we talk about the initial setup, which involves making changes to
makefile variables and the web server's configuration, we'll choose
some values that you eventually should change. For example, we call
our application "ywaitdemo" and use "127.0.0.1" as the IP address and
"localhost" as the name of the web server. We'll also use "httpd" as
the name of the program (i.e., the daemon) that runs on the web server
and delivers documents (e.g., HTML files) to clients or executes cgi
scripts for them. There's a decent chance you'll find a program named
httpd on your web server, but it's not guaranteed even if you're using
Apache.
Claiming the "system administrator" title on our server has a subtle
influence on our example. We can begin by assigning values to makefile
variables that are closely connected to httpd's configuration file and
be confident we can make httpd behave properly. You may have to reverse
the process and first discover what httpd allows and then assign values
to makefile variables that conform to those rules.
------------------
Naming Conventions
------------------
Our source file naming conventions need a few words. Files that end in
strange suffixes, like ._HTML, ._JAVA, ._PL, ._SH, ._TXT, and ._YX are
usually stream edited by a Yoix script named gsubsti.yx. The exception
is gsubsti._YX itself, which is translated into gsubsti.yx by a sed
script that's defined in make/gsubsti.mk.
Upper case suffixes mark source files that need preprocessing and the
underscore prevents name collisions on case-insensitive file systems.
In other words, when you see files like
bin/ywait_exec.pl
and
bin/ywait_exec._PL
remember that ywait_exec.pl is the Perl script that eventually will be
installed, but it's created from ywait_exec._PL, which is the official
source and the file you must edit when you need to change the script's
behavior. Accidentally put your changes in ywait_exec.pl and they will
disappear when make decides to remove or overwrite ywait_exec.pl. If
you're curious about the preprocessing step type something like
diff bin/ywait_exec._PL bin/ywait_exec.pl
and you'll see what happened.
---
There's a naming convention that we follow in Yoix scripts that we'll
briefly mention here. Yoix builtins (e.g., open(), close(), fprintf()
appendText()) always begin with a lower case letter, so when you see
a function call, like QueueCommand() or ShowScreen(), and the name of
the function begins with an upper case letter you can be sure it's not
calling a Yoix builtin. Start your Yoix function names with an upper
case letter, which is what we do in YWAIT, and the name automatically
carries "I'm not a Yoix builtin" with it.
---------------
Getting Started
---------------
Obviously this is an important file if you're just starting, but two
others are more important and you should take a look at both of them
before you build anything
Makefile
This is the top level makefile and you will use it to build
and install the entire package. Probably nothing to change
here, unless you start adding your own source directories.
The SERVER_ROOT variable that's defined in this file can be
convenient if you're maintaining a real system, so keep it
in mind, but it's not something you need right now.
make/variables.mk
If you're lucky this will be the only YWAIT source file that
you have to change. It's where important makefile variables
are initialized, usually with values that were selected when
you (or someone on your behalf) ran the YWAIT configuration
program. It's a file that's supposed to be included, either
directly or indirectly, by every Makefile in this package.
Pay attention to SERVER_SETUIDFILES - the default list will
probably work on your web server, but we recommend that you
try the empty list
SERVER_SETUIDFILES =
and stick with it if it works. The programs mentioned in the
SERVER_SETUIDFILES list are the only ones that are installed
as setuid and setgid on your web server.
There are a few other files you may want to look at if you're curious
or just want to understand what happens when you run make.
make/rules.mk
This is where you'll find definitions for targets (e.g., all
install, clean and clobber) that the low level Makefiles use.
Inference rules defined near the end of this file control the
preprocessing of source files marked by the special suffixes
that are also defined in this file.
make/common.mk
A file that's included by almost every low level Makefile
in the YWAIT source package. It's really only two lines that
include variables.mk and rules.mk.
scripts/gsubsti._YX
This is a complicated Yoix script that's used to preprocess
source files that are marked by the special suffixes that we
mentioned above (of course gsubsti._YX is an exception). The
preprocessing replaces "magic" tokens like,
<_ACRONYN_>
or
<_ACRONYM_DQ_>
with their official definitions. Most of those definitions
come from variables.mk, but a few of the "magic" tokens are
only defined in this file.
The generality that gsubsti._YX provides comes at a price.
Starting the Yoix interpreter is a non-trivial job that can
take several seconds on slower machines, and doing it over
and over again as each source file is preprocessed isn't a
particularly good "showcase" for Yoix.
--------------------
Makefile Variables I
--------------------
An important part of your job involves the initialization of makefile
variables. Four of them have to be coordinated with your web server's
configuration, so that's what we'll talk about in this section. The
four variables are SERVER_ALIAS, SERVER_SCRIPTALIAS, SERVER_DOCDIR,
and SERVER_CGIBIN. You'll find their definitions in
make/variables.mk
so that's the file we'll be updating.
SERVER_ALIAS
SERVER_SCRIPTALIAS
These are the URL paths that are mapped to real directories on
your web server. SERVER_ALIAS is for documents (i.e., HTML files
or Yoix scripts) and SERVER_SCRIPTALIAS is for cgi scripts. The
only one your users need is SERVER_ALIAS, because it's part of
the URL they use to visit your application's web site (e.g., to
get the installer for the client software). We'll set
SERVER_ALIAS = ywaitdemo
SERVER_SCRIPTALIAS = ywaitdemo-cgi
but you should pick values that are right for your application
and web server. You may have to talk to your system administrator
or dig through your web server's configuration file to find the
answer. Remember, we're the system administrator in our example,
so we can set the values right now and worry about making the web
server behave in the next section. You probably won't be so lucky.
SERVER_CGIBIN
SERVER_DOCDIR
Three variables control where server software is installed and two
of them (i.e., SERVER_CGIBIN and SERVER_DOCDIR) will be needed when
we configure httpd. We'll set
SERVER_CGIBIN = /var/www/ywaitdemo/cgi-bin
SERVER_DOCDIR = /var/www/ywaitdemo/htdocs
but once again you should to pick directories that are right for
your application and web server. SERVER_DOCDIR is where the web
server will look for documents that describe your application and
Yoix scripts that YWAIT's client software will need. SERVER_CGIBIN
is where two important cgi scripts go. One handles login and the
other takes care of almost everything else that goes on between
clients and your server.
--------------
The Web Server
--------------
Clients that want to run your application eventually have to talk to
your web server, so that's what we'll focus on in this section. We're
not "web server" experts and we really have only experimented with the
Apache server, so it's the only one we're even remotely qualified to
talk about. Our lack of "web server" expertise means this section will
be mercifully short, however there's lots of good documentation about
Apache on the web, so you won't have any trouble filling in the gaps.
---
We will assume httpd is running and someone has already handled the
initial setup, so all we have to do is add a few lines to the httpd
configuration file. Your first job is to find that file. A reasonable
guess is
/etc/httpd/conf/httpd.conf
but if that's not right you'll have some detective work to do, or you
could just ask someone. We think you should make a copy of httpd.conf
before you change it, but it's your choice and your mess to clean up
if something goes wrong.
When you're ready find the "Alias" section in your httpd.conf and add
something like
Alias /ywaitdemo/ /var/www/ywaitdemo/htdocs/
AllowOverride None
Options None
Order allow,deny
Allow from all
Notice how the SERVER_ALIAS and SERVER_DOCDIR definitions that were
discussed in the last section are used here - you obviously should
substitute your values. Next find the "ScriptAlias" section and add
something like
ScriptAlias /ywaitdemo-cgi/ /var/www/ywaitdemo/cgi-bin/
AllowOverride None
Options None
Order allow,deny
Allow from all
Notice how our SERVER_SCRIPTALIAS and SERVER_CGIBIN definitions were
used here - you obviously should substitute values that you assigned
to those variables.
---
That's pretty much it. Write the config file out and then tell httpd
(if it's really running) to read the new config file. We do it on our
system by typing something like,
/usr/sbin/httpd -krestart
but check the httpd man page or talk to your system administrator. If
you're lucky everything will just work, but if it doesn't you'll need
the httpd logs files (access_log and error_log). In fact, the httpd
logs are sometimes the only way to track down problems (e.g., errors
in scripts should end up in error_log), so we recommend that you take
a quick look at the ones on your system. They're in
/var/log/httpd
on our Linux web server, but they're also mentioned in httpd.conf, so
you shouldn't have any trouble finding them.
---------------------
Makefile Variables II
---------------------
Make sure you're happy with the variable definitions in
make/variables.mk
before you use make to build and install YWAIT on your web server. We
discussed four important makefile variables several sections ago, but
others may also need your attention. We'll list them in alphabetical
order, even though some are clearly more important than others, and
we'll talk about the ones that still need an explanation.
---
ACRONYM
ACRONYM_LOWER
ACRONYM_UPLOW
Short names that are associated with your application in upper,
lower, and mixed case. ACRONYM_LOWER and ACRONYM_UPLOW are used
in pathnames and Java class names, so avoid spaces or other funny
characters that could cause problems. There currently aren't any
restrictions on ACRONYM, but it's probably best if you follow the
rules that apply to ACRONYM_LOWER and ACRONYM_UPLOW. We named our
application "ywaitdemo", so we would set
ACRONYM = YWAITDEMO
ACRONYM_LOWER = ywaitdemo
ACRONYM_UPLOW = YwaitDemo
but you eventually should come up with a better name.
BETA_PREFIX
This is an obscure variable that we occasionally use for testing
production systems. We set it to
BETA_PREFIX =
and we recommend you do the same thing.
FULLNAME
This is a long name of your application. It's not unusual to set it
to ACRONYM_UPLOW, but characters like spaces that can't be used in
ACRONYM_UPLOW don't cause problems in FULLNAME. We set
FULLNAME = Ywait Demo
but you should pick a value that's right for your application.
GSUBSCRIPT_BASENAME
This is the base name of the Yoix script that stream edits source
files that are marked by the suffixes that we discussed earlier in
this file (e.g., ._JAVA, ,_HTML, ._PL, ._SH, ._YX). We set it to
GSUBSCRIPT_BASENAME = gsubsti
and there's absolutely no reason to set it to anything else.
JARFILE_BASENAME
This is the base name of the jar file that gets loaded on client
machines when they run yoix application's installer. We usually
assign the same value to JARFILE_BASENAME and ACRONYM_UPLOW, so
we would do
JARFILE_BASENAME = YwaitDemo
or
JARFILE_BASENAME = $(ACRONYM_UPLOW)
but you should pick a value that's right for your application.
JARFILE_RELEASE
JARFILE_RELEASE should be set to X.Y.Z (where X, Y, Z are digits).
It's used to identify the jar file that's installed on a client's
system and is also used in etc/ywait_rc._PL to initialize the list
of allowed releases. We set
JARFILE_RELEASE = 1.0.0
and for the time being you might as well do the same thing.
JAVA_BIN
You need java, javac (the compiler), and jar to build and install,
and our Makefiles assume all three can be found in JAVA_BIN. We set
it to
JAVA_BIN = /usr/local/java/bin
but you must provide the right directory for your system. Remember,
we recommend Java 1.5.0 if you plan on running the client software
on your server, otherwise some variation of Java 1.4.X should let
you finish the build.
OWNER
The name of the owner of this application, typically the name of
the company paying your salary. The SUBOWNER field can be used to
provide more detail. We work for AT&T, so we would put
OWNER = AT\&T
but you can leave it blank or fill it in as you see fit. Notice
the backslash in front of the ampersand - it's needed because we
use OWNER in a sed script and & means something special to sed
if it's not escaped.
PERL_PATH
Almost all the scripts that run on the server are written in Perl,
so you definitely will need a decent version of Perl. PERL_PATH
should be the full path name of the perl executable that should
be used. We set it to
PERL_PATH = /usr/bin/perl
but you must supply a value that's right for your web server.
PROPRIETARY_LABEL
Text that appears at the bottom of almost every system screen. We
often use it for a proprietary label, but you're free to use it for
whatever you want. An empty label is allowed and is often the right
choice.
SERVER_ALIAS
Discussed in an earlier section.
SERVER_BASEURL
SERVER_BASEURL should be the leading portion of your application's
URL and it obviously must be coordinated with your web server. This
is also where you set the protocol (e.g., https) and port that your
web server uses. We recommend IP addresses rather than relying on
DNS, so we would set it to
SERVER_BASEURL = http://127.0.0.1
but you must provide a value (protocol, address, and port) that's
coordinated with your web server.
SERVER_CGIBIN
Discussed in an earlier section.
SERVER_DOCDIR
Discussed in an earlier section.
SERVER_HOMEDIR
We discussed SERVER_CGIBIN and SERVER_DOCDIR several sections ago,
but most files and directories installed on your web server end up
somewhere under SERVER_HOMEDIR. We like it when SERVER_CGIBIN and
SERVER_DOCDIR are subdirectories of SERVER_HOMEDIR, so we set
SERVER_HOMEDIR = /var/www/ywaitdemo
but it's not required and your system administrator may have rules
that separate the directories. Three different makefile variables
that control where files are installed should let you comply with
any rules your web server throws at you.
SERVER_SCRIPTALIAS
Discussed in an earlier section.
SERVER_SETUIDFILES
SERVER_SETUIDFILES are the names of executables that are installed
as setuid and setgid programs. The default list
SERVER_SETUIDFILES = ywait_exec ywait_login
probably works on all web servers, but it should be set to
SERVER_SETUIDFILES =
if your web server handles things for you (i.e., runs cgi scripts
as you). Try the empty list if you're not sure what's needed and
stick with it if it works. Your system administrator undoubtedly
can help and both of you may want to take a close look at
bin/ywait_exec._C
and
bin/ywait_login._C
which are the source files for our default setuid programs. Both
of them are very simple C programs, so you shouldn't have trouble
understanding them.
SERVER_TYPE
This should be set to PRODUCTION, DEMONSTRATION, DEVELOPMENT, or
PROTOTYPE. It controls messages users see when they login and the
default colors used for things like screen backgrounds. We set
SERVER_TYPE = DEMONSTRATION
but you can pick any one of the four values that's appropriate for
your application.
SUBOWNER
The name of the organization within OWNER that is the owner of this
application. We work for AT&T Research, so we put
SUBOWNER = Research
but you can leave it blank or fill it in as you see fit.
USER_NAME
This is a variable that gives you a little control over the login
process used by your application. Setting it to NULL means everyone
must supply a name and password that's validated by your web server.
The default validation is handled by a Perl script that's installed
in the subs directory and it uses a passwd file that's installed in
the etc directory. The passwd file that YWAIT supplies has a single
entry for a user named "guest" with an empty password field, which
is a convention that lets the next login as that user, in this case
"guest", set the password. We set
USER_NAME = NULL
and to start with we recommend that you do the same, at least until
your application is really works. After that you have choices. For
example,
USER_NAME =
or
USER_NAME = unknown
means your application will use a login screen that just asks for
a user name, however you still have some work to do on your server
before users will get past the login screen. One approach is to add
a "magic" entry to your installed passwd file (see the comments in
the passwd file), while another approach would be to edit the Perl
script in the subs directory that handles user validation. Neither
one is difficult, but we decided that automating everything based
on the value assigned to USER_NAME in variables.mk probably would
not be a great idea.
------------
Installation
------------
When you're comfortable with the definitions in variables.mk return to
your top level source directory (it's where you originally found this
file) and type
make
or
make all
to build everything and
make install
if you want to build and install everything in one step. In practice,
we usually use
make all install
because it completely separates the build and install steps, and that
gives make a chance to quit before anything is installed if the build
fails. Along the same lines, we usually do
make clean all install
or
make clobber all install
when we want to rebuild and install everything (or almost everything).
The clean and clobber targets remove things that make built, and right
now there's only a small difference between them - gsubsti.yx and jar
files built by make and left in the jars directory are removed when
you use clobber but not when you use clean. Most of the time clean is
the right choice.
---
Incidentally, every file named Makefile in the YWAIT source package can
be used this way. For example, type
cd htdocs
make all install
and you only build and install the files that are in the htdocs source
directory. One small point deserves mention - using clobber instead of
clean in a subdirectory
cd htdocs
make clobber
removes gsubsti.yx, which indirectly affects other source directories.
-------------
The Installer
-------------
One of the things you get after you build and install is a simple web
page that will direct users to a special jar file that they can use to
install all the software (except for Java) that they need to connect
to your server. In our case we end up with two html files
index.html
YwaitDemo.html
and the installer (i.e., the executable jar file)
YwaitDemo.jar
in the htdocs directory under SERVER_DOCDIR.
Users can read about your application (i.e., whatever's in index.html)
by pointing their browser at your server's URL. In our case we would
tell users to go to
http://localhost/
to read about ywaitdemo and find links to the executable jar file that
installs the client software on their system.
We recommend you try the entire process, because if your browser has
trouble finding the installed index.html file then something may be
wrong with (or missing from) the changes that you made to the httpd
configuration file. You might as well track the problem down because
your clients undoubtedly will have trouble too. In this case the best
debugging advice we can give you is to make sure httpd is running and
make use of the httpd log files (access_log and error_log). As usual,
your system administrator may have to help, particularly if you can't
find or read log files.
------------------
Testing The Server
------------------
After you run the installer you probably should test your new web site,
so find the bin directory that was created by the installer and run the
script that has the name that you assigned to the ACRONYM_UPLOW variable
in your Makefile. In our case we would run
YwaitDemo
and if things work we would see a login screen for the application. If
you get to the login screen and you haven't done anything to the passwd
file (it's installed in the etc directory) then "guest" (without quotes)
is be the only name that will be recognized and the first password that
you send for "guest" will end up as the official password. There's much
more info about the simple passwd file that YWAIT supplies in etc/README.
---------------
Server Problems
---------------
If you're lucky everything is working and you can skip this section,
but server problems aren't unusual, so we'll outline some debugging
techniques that might help.
---
The httpd logs are always useful and they're often the best place to
start, but you'll undoubtedly need help from your system administrator
if you can't find them or you're not allowed to read the logs. Nothing
will work if your web server isn't listening, so make sure httpd (or
whatever the daemon happens to be called on your server) is running.
If you want to be thorough make sure you and httpd agree about the IP
address and port that you're using.
---
YWAIT has a log file that the cgi scripts update. Ours would be
/var/www/ywaitdemo/admin/logs/access_log
but you'll find yours in the admin/logs directory under SERVER_HOMEDIR.
Actually, the Perl scripts kicked off by the two cgi scripts are really
responsible for updating the log file and they do it using a subroutine
named WriteLog() that's defined in your system's rc file. The YWAIT log
file is where important information about a running system goes, but it
usually won't provide much help if you're struggling with a system that
isn't responding.
---
Your browser can be a useful debugging tool, so an easy thing to try
is point it at the Yoix script that starts your application. In our
example the URL would be,
http://localhost/ywaitdemo/ywaitdemo.yx
but you will have to make the obvious changes for your application. If
you followed our advice and used an IP address in your SERVER_BASEURL
definition then make sure you also run the browser test with a URL that
uses that IP address. The URL to test in our example is
http://127.0.0.1/ywaitdemo/ywaitdemo.yx
but, as usual, you need to substitute values that are appropriate for
your application. Notice how we used SERVER_BASEURL and SERVER_ALIAS
to build up the first part of the URL. If your browser displays the
file then httpd is probably happy with the SERVER_DOCDIR portion of
your web site, but if either test fails go to the httpd logs or your
system administrator for help.
---
It's also easy to run a quick test of your cgi scripts, but in this
case many more things can go wrong, so success isn't a guarantee that
your scripts will work when they start handling real work. Point your
browser at the login script, which in our example would be
http://127.0.0.1/ywaitdemo-cgi/ywaitdemo_login.cgi
and if you're lucky you'll see,
STATUS=696e7465726e616c6572726f72
REASON=6e6f20696e70757420737570706c696564
which is a hex-encoded message that says
STATUS=internalerror
REASON=no input supplied
As usual, you should substitute values that are appropriate for your
application. Notice how we used SERVER_BASEURL and SERVER_SCRIPTALIAS
to build up the first part of the URL. If your browser displays the
error message from the login script then httpd is probably happy with
the SERVER_CGIBIN portion of your web site, otherwise go to the httpd
logs or your system administrator for help.
---
The YWAIT source package includes the Yoix interpreter in an executable
jar file (i.e., jars/yoix.jar), so if you type something like
java -jar jars/yoix.jar http://127.0.0.1/ywaitdemo/ywaitdemo.yx
and you're in the top level source directory you eventually should see
your application's login screen. As usual, you should substitute values
that are appropriate for your application, which might also include the
full path to java. If the login screen doesn't show up then something
must still be wrong with your web site, so look at the httpd log files
or talk to your system administrator.
---
Sometimes you can get to the login screen but no farther and the usual
suspects in this case are Perl problems (e.g., a missing module), file
permission problems, or a problem with your passwd file. A quick look
at your web server's error log will usually uncover Perl problems. If
you don't have access to the httpd logs try redirecting stderr in the
cgi script that handles logins or talk to your system administrator.
---
Suspect file permission problems if you changed SERVER_SETUIDFILES and
in that case you probably should restore the default value
SERVER_SETUIDFILES = ywait_exec ywait_login
in variables.mk and install again. If you want to do it quickly use the
Makefile in the bin directory to build and install, but if you're in a
real hurry you could just type,
chmod 6755 /var/www/ywaitdemo/bin/ywait_exec
chmod 6755 /var/www/ywaitdemo/bin/ywait_login
as long as you remember to update the SERVER_SETUIDFILES definition in
variables.mk if this fixes your problem.
---
If you still can't login find your installed passwd file. Ours is
/var/www/ywaitdemo/etc/passwd
but yours will be installed in the etc directory under SERVER_HOMEDIR.
If the only uncommented line in the file is
guest::shared:0:0:::Guest Login::
then nobody has logged in as "guest" and it's unlikely the passwd file
is causing problems - the empty password field simply means the next
login as "guest" sets the password and nobody has done it yet. On the
other hand if there's more than one entry for "guest" and the last one
looks something like
guest:2sLpZAbxIdm5k:shared:1234567890:0:::Guest Login::
then someone (most likely you) has already logged in as "guest" and you
don't know the password. In this case an easy fix is to delete the last
"guest" entry and try logging in again. You should also look for a lock
file named "passwd.lck" in the same directory as your passwd file. If
you find one remove it and try logging in again.
---
If you're really desperate make SERVER_HOMEDIR and everything under it
readable and writable by everyone. It's definitely not the way to run
a production system, but it's a brute force approach that eliminates
file permissions as a source of your problems. Remember, this isn't a
permanent fix - find the real problem is if this is the "magic" that
makes everything else work.
--------------------
It Works - Now What?
--------------------
It's often instructive to watch the official YWAIT log file while you
experiment, so we suggest you type something like
tail -f /var/www/ywaitdemo/admin/logs/access_log
in a terminal window and keep that window somewhere you can see while
you're testing your application. A variable named LOGDETAIL gives you
some control over how much detail ends up in the log. It's defined in
the file
/var/www/ywaitdemo/etc/ywaitdemo_rc.pl
on our server and it can be changed while the system is running. Make
it larger (e.g., 5) to get more detail and smaller to get less. It's
also occasionally interesting to watch logs files that httpd updates.
---
Try some simple screens first. Select "Help->About", "Show->MOTD", and
"Show->Logins" from the main screen's menubar and you'll get a look at
three pretty simple screens. The "MOTD" and "Logins" screens are built
from the same screen file, namely
/var/www/ywaitdemo/screens/ywaitdemo_viewer.yx
while the "About" screen is built from
/var/www/ywaitdemo/htdocs/ywaitdemo_notify.yx
which is an important screen file that's also used for alerts, errors,
and warnings.
Old time Unix users may remember the "message of the day" file that was
stored in /etc/motd and displayed after a successful login. YWAIT does
something similar in its "Welcome" screen when it finds a file in
/var/www/ywaitdemo/etc/motd
and also lets you look at that file when you select "Show->MOTD" from
the main screen's menubar. Create a motd file on your server and see
what happens. An empty or missing motd file means version information
is displayed as the "message of the day".
---
Screens usually let you drag text out of components, like a JTextArea
or JTextField, but only a few currently let you drop text in them. The
clipboard and the screens used to send messages to users or comments to
administrators have JTextAreas that can be the "source" or the "target"
of a drag and drop operation. Select "Show->Clipboard" from the menubar
on your main screen and verify that the drag and drop operations really
work.
Incidentally, you can find the three screens that accept dropped text
by looking for event handlers named dragEnter() or drop() in the Yoix
scripts that YWAIT supplies. For example, type
grep 'dragEnter(' [a-z]*/*
in your source top level source directory and you'll find them.
---
The debugger screen is another one you should try. The JComboBox in
the lower right corner of the debugger screen determines what happens
when the "Execute" button is pressed. Scroll through the selections
in the JComboBox, pick "Test Plugin 1" or "Test Plugin 2", and press
the "Execute" button. If a small window pops up hit the button labeled
"Test" and you may get a chance to play with some interesting examples.
Unfortunately we can't always distribute the software that the plugin
tests need, so there's a chance nothing will happen.
The two JComboBox selections labeled "Reset Screens" and "Reset All"
clear data structures that clients use to cache "screens" and other
information that was downloaded from the server. They're often used
when we modify screen files on the server and then want to test the
changes on a client that's already running. The obvious alternative,
which always works, is log back in again whenever there's a change
that needs to be tested.
You can see how they work without actually changing a screen file.
Select "Show->MOTD" from the main screen's menubar and the "Message
Of The Day" screen should be displayed, but it's only downloaded from
the server once. The easiest way to prove it is by watching the YWAIT
log file - you'll see an entry with "GETSCREEN" as the command field
when the client decides it needs to ask the server for a new screen.
Select "Reset Screens" in the JComboBox, press the "Execute" button,
and you'll see a "GETSCREEN" command the next time you show the MOTD
screen. If you're really ambitious, manually change something, like
the background color of the JTextArea, that you'll notice. We would
do it in the screen file
/var/www/ywaitdemo/screens/ywaitdemo_viewer.yx
that's installed on our server.
---
Everything that you find under "Examples" in your main screen's menubar
is worth a try. The "Examples->Template" and "Examples->Server" screens
are examples that should help if you're interested in customizing YWAIT
and are ready to start looking though some Yoix code. The templates are
screen files you can copy and modify to suit your needs, and the server
screens are filled with comments and are really "hooked up" to scripts
that run on your server.
---
Try sending yourself a message. Select "Send->Message" from the main
screen's menubar and a simple screen should appear that lets you type
a text message and send it to a user of your application. Try it and
see if you can find the message sitting on your server. If you want
to read your messages select "Show->Messages" from your main screen's
menubar or press the small "envelope" icon that should have appeared
near the lower right corner of the main screen.
Imagining improvements or alternatives to the simple message system
isn't hard and it's probably something we'll add in the near future.
Send us mail if it's something you're really interested in.
---
See if you can change your password. Select "Send->Password" from the
main screen's menubar and a screen, which in this case will be a modal
dialog (i.e., you have to hide it to do other things), will appear and
you can use that screen to change your password. Some checking on the
server probably will reject requests to change the guest password. The
default password file that comes with YWAIT supports user named "guest"
that has an empty password field (i.e., first login sets the password),
but "guest" also belongs to a group named "shared" and that's a group
that can't change their password once it's set.
Change the group field from "shared" to "default" in the last passwd
file entry for the "guest" user you eventually will be able to change
the password. The reason we said "eventually" is because you will be
thrown off the system and forced to log back in if the group changes
while you have an active session. Anyway, log back in and then try to
change the password again and this time it should work. Incidentally,
a variable named PASSWORDTIMER that's defined in
/var/www/ywaitdemo/etc/ywaitdemo_rc.pl
won't let you change it too often.
---
The screen shown when you select "File->Preferences" lets you change
user specific settings (e.g., colors, fonts) and gives you a chance
to save your new settings on the server. It's a very complex screen,
so at this point you probably shouldn't spend time reading the Yoix
code that implements the screen, but you definitely should try it.
Incidentally, the "shared" group, which is the default group for the
guest login, may be able to save preferences on the server, but it's
something that probably will be changed in the very near future, so
check the passwd file if you have trouble saving preferences on the
server.
---
Take a look at the "Send->Update" screen. It's worth a quick try if
you're ready to quit. The mechanism used to download and install the
new jar file after you select "Send->Update" is also used to handle
automatic updates when the server decides that a client is using a
jar file that's too old. That mechanism is controlled by variables,
like ALLOWED_RELEASES and MINIMUM_RELEASE, defined in the file
/var/www/ywaitdemo/etc/ywaitdemo_rc.pl
that also must be coordinated with specially encoded jar files that
are stored in directory
/var/www/ywaitdemo/admin/updates
on the server.
---------
Exploring
---------
The demo system that we distribute and you just installed and tested
is substantial, but it currently doesn't include an application that
you should find compelling. That eventually will change, but for now
it's primarily a system that you can experiment with encapsulated in
a framework that's not too hard to expand and customize once you're
familiar with the YWAIT architecture.
Many important files installed on your server are heavily commented
(e.g., bin/ywaitdemo_exec.pl), so they're a good source of information
and comments are easy to toss if you're really running a production
system. Most directories in the source package have README files and
some contain information that you won't find anywhere else, so poke
around in the important source directories (e.g., etc, bin, cgi-bin,
htdocs, screens, subs) and see what their README files say. A point
to keep in mind while you explore is that Perl, shell, and C programs
run on the server, but Yoix scripts are downloaded and executed by the
Yoix interpreter that's running on the client.
---
Here's a list of some interesting files, in no particular order, that
are currently installed on our web server. As usual replace ywaitdemo
with your application's name and look for them in your SERVER_CGIBIN,
SERVER_DOCDIR, and SERVER_HOMEDIR directories.
cgi-bin/ywaitdemo_exec.cgi
cgi-bin/ywaitdemo_login.cgi
Small shell scripts that let you adjust system resources before
the Perl scripts that do the real work are kicked off. They can
also be useful for debugging. For example, change the line
/var/www/ywaitdemo/bin/ywaitdemo_exec
in ywaitdemo_exec.cgi to
/var/www/ywaitdemo/bin/ywaitdemo_exec 2>>/tmp/error_log
and errors (or debugging information written to stderr) end up
in /tmp/error_log rather than in httpd's error log.
bin/ywaitdemo_exec
bin/ywaitdemo_login
Small C programs called by the cgi scripts that exec() the Perl
scripts that handle the real work. They exist because web servers
don't always run your cgi scripts as "you" and operating systems
usually won't recognize setuid or setgid scripts. Install them as
setuid and setgid executables when httpd isn't cooperating and the
Perl scripts they exec() should run as "you".
It's the SERVER_SETUIDFILES makefile variable that picks the files
that are installed setuid and setgid, and if you decide that your
web server needs setuid help we recommend you look at the source
files
bin/ywait_exec._C
and
bin/ywait_login._C
to make sure you understand what they're doing.
bin/ywaitdemo_exec.pl
bin/ywaitdemo_login.pl
These are the two Perl scripts that end up handling the login and
command execution requests that clients send to your cgi scripts.
The ywaitdemo_exec.pl script is the most interesting and it's the
one you change when you add new screens or new capabilities to your
application. We included lots of comments, so if you know Perl you
probably won't have too much trouble following the code.
The ywaitdemo_login.pl script isn't nearly as interesting and you'll
probably also find that it's harder to follow. The good news is you
usually don't touch this file because the low level user validation
is handled by
/var/www/ywaitdemo/subs/ywaitdemo_validation.pl
and that's where any customization goes.
etc/ywaitdemo_rc.pl
An important Perl script that's "included" by the two Perl scripts
that your cgi scripts call (indirectly). Lots of comments and some
variables (e.g., RUNSTATE, LOGDETAIL, ALLOWED_RELEASES, PINGTIMER)
that you occasionally adjust while the system is running.
WriteHeader() and WriteLog() probably are the subroutines you'll
use most. Every response that goes back to a client request must
call WriteHeader() before ever sending any other data. WriteLog()
appends stuff to the main log file and can be useful when you're
debugging Perl scripts.
Take a look at ValidateUser() if you're curious about the passwd
file and the default user validation that YWAIT provides. The call
that actually validates users when they login comes from
/var/www/ywaitdemo/subs/ywaitdemo_validation.pl
which is a Perl script that you can modify to suit your own needs.
etc/passwd
This is the password file that let you login. You should now find
two entries for a user named "guest". The first entry has an empty
password field (i.e., ::) but the second has that field filled in
based on the password you supplied the first time you logged in.
The ValidateUser() subroutine that's defined in the rc file reads
your passwd file and always uses the last entry that it finds for
a particular user, so new entries can be appended to the passwd
file while the system is running. YWAIT provides a way to clean
your passwd file (see the admin/bin scripts) and it's convenient
let cron do the work for you.
---
Our Perl scripts always create a lock file
/var/www/ywaitdemo/etc/passwd.lck
that contains their process id before they update the passwd file,
so a cron job that cleans the passwd file and ValidateUser() that
needs to fill an empty password in don't interfere with each other.
If you need to manually edit the passwd file we recommend you type
something like,
>>/var/www/ywaitdemo/etc/passwd.lck
and only edit the passwd file if passwd.lck really is empty. Also
don't forget to remove the passwd.lck file that you created when
you're done.
---
We have a partially complete Yoix script that lets you update the
passwd file using a JTable that's displayed in a JFrame - let us
know if it's something you really want and we'll either send it to
you or move it up on our list of things to do.
admin/logs/access_log
This is your application's main log file and entries are created
by the WriteLog() subroutine defined in your rc file. It's often
interesting to do
tail -f /var/www/ywaitdemo/admin/logs/access_log
in one window and watch the log grow as you, or others, do things
in your application. A variable named LOGDETAIL lets you control
how much detail ends up in the log. It's defined in your rc file
can be changed while your system is running. Make it bigger to get
more detail and smaller to get less. For example, the call
WriteLog("-d3", "this is a test...");
only ends up in access_log when LOGDETAIL is greater than or equal
to 3. Omitting "-d3" in the WriteLog() call
WriteLog("this is a test...");
is equivalent to supplying "-d0".
htdocs/ywaitdemo.yx
Your application starts running when clients point an appropriate
version of the Yoix interpreter at this file. It's automatic when
clients use the scripts and jar file that your installer loads on
their system.
This file and the others that it explicitly includes provide the
low level infrastructure that a YWAIT application needs. Actually
"low level" may not be an appropriate term, because the included
files are Yoix scripts that you can easily read or modify. Take
a quick look at ywaitdemo.yx and you'll see something like
include ServerFile("ywaitdemo_common.yx");
include ServerFile("ywaitdemo_icons.yx");
include ServerFile("ywaitdemo_insets.yx");
include ServerFile("ywaitdemo_plugins.yx");
include ServerFile("ywaitdemo_preferences.yx");
include ServerFile("ywaitdemo_screens.yx");
include ServerFile("ywaitdemo_server.yx");
include ServerFile("ywaitdemo_threads.yx");
include ServerFile("ywaitdemo_custom.yx");
near the beginning of the file. These are the include statements
that download the Yoix scripts that build the YWAIT infrastructure.
A good one to look at is ywaitdemo_insets.yx, because it's short
and illustrates some Yoix techniques that we use elsewhere. The
ones that are particularly important, like ywaitdemo_screens.yx
and ywaitdemo_server.yx, are discussed later.
htdocs/ywaitdemo_server.yx
This is the file that handles communications when a client wants
to talk to your server. It's included by ywaitdemo.yx and usually
ends up "talking" to your bin/ywaitdemo_exec.pl Perl script. The
low level details are handled by PostCommand(). It's a long and
rather complicated Yoix function, but it's important and worth
the effort if you decide to figure it out.
Screens that communicate with the sever never call PostCommand()
directly, but instead they use QueueCommand() or RunCommand(),
which are also defined in this file. Both call PostCommand() if
they need to talk to the server. QueueCommand() arranges for a
special thread to call RunCommand(), which means QueueCommand()
doesn't block or wait for the server to respond. RunCommand() is
is much more patient - it waits for its turn in PostCommand() and
doesn't return until the server has responded.
Add some strategic fprintf() calls to the file installed on your
server and you will see the output the next time you start your
` application. For example, put something like
fprintf(stderr, "header=%O\n", header);
after the "while" loop in PostCommand() that reads the response
from the server and the contents of header Dictionary will print
on stderr in the window that you used to start your application.
The %O option is a special Yoix addition that asks fprintf() to
dump the argument in a way that depends on the argument's type.
It can be a quick way to get debugging information that doesn't
force you to know anything about the object you're dumping.
If you prefer seeing the same information in dialog try,
ShowText(strfmt("header=%O\n", header), TRUE);
but it's a modal dialog that you have to dismiss when you want to
continue. The optional TRUE argument tells ShowText() the string
is formatted with newlines and whitespace and should be displayed
as is.
htdocs/ywaitdemo_screens.yx
This is another important file and it's one that you have to deal
with when you start building custom screens. The first section of
the file (about 650 lines or so) defines important functions, like
GetScreen(), GetAndShowScreen(), HideScreen(), LoadScreen(), and
LoadArgs() and also includes several dictionaries that are used to
manage screens.
The rest of the file defines convenient functions, like ShowAlert()
and ShowViewer(), that also hide ShowScreen() calls that sometimes
are complicated. The number of "helper" functions often grows as we
build a production system with lots of custom screens, so don't be
surprised if you start adding your own, and you can do it in this
file or in htdocs/ywaitdemo_custom.yx.
---
Screen files are special Yoix scripts that have following general
structure
import yoix.*.*;
Dictionary Global = argv[1];
Builder(String name, Array args) {
JFrame screen = {
//
// Initialize some of JFrame variables.
//
Dimension size = NULL;
Object parent = NULL;
Font font = Global.GetPreferredTextFont();
//
// Every screen also must define and initialize the
// following special variables.
//
String screenname = name; // required
String screentitle = NULL;
Object anchorpoint = NULL;
int screenanchor = CENTER;
int initialized = FALSE;
int disposeonclose = TRUE;
int savestate = TRUE;
int reloadargs = FALSE;
//
// We almost always use GridBagLayout to arrange
// a screen's top-level components. It's the most
// powerful layout manager that Java provides, but
// it takes some time to get used to. Yoix or Java
// documentation will help, but experimenting with
// a working screen (you now have lots of them) is
// often the best way to learn.
//
GridBagLayout layoutmanager;
Array layout = {
//
// The initializer here should be a comma
// separated list of expressions arranged
// in pairs
//
// new JButton {
// ...
// },
// new GridBagConstraints {
// ...
// },
//
// that creates the component and constraint
// used to position the component. A simple
// screen, like ywaitdemo_template3.yx, might
// have five or six different components.
//
};
};
return(screen);
}
return(Builder); // execute() gets this
In other words, a screen file defines a function named Builder()
that accepts two arguments and returns a screen (e.g., JFrame or
JDialog) that's built using the arguments and a Dictionary named
Global. However it's the line that initializes Global and the one
that returns Builder that are fundamental, and to understand them
we need to take a look at some low level screen functions. Along
the way we'll also discover what's in the Builder function's args
array.
---
GetScreen() is the best place to start, so here's a "stripped down"
version
GetScreen(String name, String builder, ...) {
Dictionary dict;
Object screen;
Array args = &builder + 1;
if ((screen = GetCachedScreen(name)) == NULL) {
if ((dict = LoadScreen(builder)) != NULL) {
if (dict.Builder != NULL) {
screen = dict.Builder(name, args)
LoadArgs(screen, args);
if (dict.cachescreen)
ScreenCache[name] = screen;
}
}
}
return(screen);
}
The first thing to notice are the arguments. The two strings are
easy, but the ... might look strange. It's the way Yoix functions
specify optional arguments and should be familiar if you're a C
or C++ programmer.
The initialization of args might be confusing, so here's a quick
explanation. Arguments in a Yoix function are stored in an Array
that's officially called argv, and when you take the address of
an argument you essentially get another array that's the subset
of argv that starts at that argument. Add 1, as we did in
&builder + 1
and the array starts at the argument that follow builder, which
means it's an array that contains the optional arguments. Actually
our explanation isn't quite right, but the result is essentially
correct, so it's good enough for now.
Notice how GetScreen() uses name. GetCachedScreen(name) looks for
name in the ScreenCache dictionary and returns NULL if it doesn't
find a usable screen associated with name. So we only build a new
screen, which might also involve downloading data from the server,
if the one we're interested in (i.e., name) isn't currently cached.
In other words, name is for finding cached screens and it provides
a way to manage screens that may or may not be created by different
Builder() functions.
---
We have to look at part of LoadScreen() if we want to understand
the builder argument, so here's a "stripped down" version of that
function
LoadScreen(String builder) {
Dictionary dict;
String fullname;
Array data;
if ((dict = GetScreenDirectoryEntry(builder)) != NULL) {
if (dict.Builder == NULL) {
fullname = "ywaitdemo_" + builder + ".yx";
data = RunCommand(NULL, "GETSCREEN", fullname);
dict.Builder = execute(data[0], fullname, global);
}
}
return(dict);
}
If Builder is missing from the special dictionary that's returned
by GetScreenDirectoryEntry() we ask the server for help. First we
create a string using builder that looks like it's the name of a
Yoix script. For example, if the builder argument is "viewer" then
our RunCommand() call would be,
data = RunCommand(NULL, "GETSCREEN", "ywaitdemo_viewer.yx");
and if you follow RunCommand() back to the code in
/var/www/ywaitdemo/bin/ywaitdemo_exec.pl
that handles "GETSCREEN" you would see we get the contents of the
screen file
/var/www/ywaitdemo/screens/ywaitdemo_viewer.yx
as the first (and only) string in the data array. Executing that
string using
dict.Builder = execute(data[0], fullname, global);
hands our global dictionary to the screen file (as argv[1]) and the
assignment to dict.Builder means we remember the function that the
screen file returned.
---
Now back to GetScreen(). If LoadScreen() returns a dictionary that
defines a Builder function we call it and use the return value as
our new screen. The line we're talking about is
screen = dict.Builder(name, args);
but Builder() functions normally ignore args and only save a copy
of name in the screen they create because HideScreen() needs it to
make sure ScreenCache is cleaned up when the screen is officially
hidden. So we have to go to the next line in GetScreen(), namely
LoadArgs(screen, args);
and look at a modified version of LoadArgs
LoadArgs(Object screen, Array args) {
int n;
for (n = 0; n < args@sizeof - 1; n += 2) {
if (args[n] instanceof String && defined(args[n], screen))
screen[args[n]] = args[n+1];
}
}
to figure out what the optional arguments are.
Even if you don't know Yoix you probably can figure this one out.
The elements in args are name/value pairs that initialize fields
in the screen. The name (i.e., the element at the even index) must
be a string and
defined(args[n], screen)
means it also must be the name of a field that's defined in screen.
If both tests are passed then
screen[args[n]] = args[n+1];
assigns the second element of the pair to that field. For example,
if args was the array
Array args = {"background", Color.green};
then the initialization in LoadArgs() would be
screen["background"] = Color.green;
which is exactly the same as
screen.background = Color.green;
htdocs/ywaitdemo_common.yx
Some generally useful functions collected together in one file.
Generic event handlers, like DragGestureRecognized(), KeyTyped()
or MouseWheelMoved() are interesting. Find their definitions in
this file and then look for calls in screen files and you'll see
how we use them. Add some debugging code, like
fprintf(stderr, "root=%O\n", root);
which dumps lots of stuff, or
fprintf(stderr, "screen title=%s\n", root.title);
to any one of the generic event handlers that are defined in the
ywaitdemo_common.yx file that's installed on your server, and the
next time you login you will see the message written to stderr in
the window that you used to start your application.
Incidentally, we picked "root" in our debugging code to emphasize
a point about Yoix functions. You won't find a variable named root
defined in the generic event handlers, so where's it coming from?
It's an important point, so see if you can figure it out, but if
you can't here's a hint. The place where the interpreter actually
found the function that it's executing (e.g., a JTextArea), which
is also known as "this", is automatically searched when a variable
like root isn't found.
screens/ywaitdemo_server_example*.yx
These are screen files that really are "hooked up" to your server.
Enter things in textfields, press send buttons and your web server
returns data that's automatically displayed by other components in
the screen (e.g., JTextArea or JTable). There are lots of comments
in these files and they interact with sections of the Perl script
/var/www/ywaitdemo/bin/ywaitdemo_exec.pl
that are also heavily commented.
The examples are all instructive, but the first is the easiest, so
concentrate on that one if you're just getting started. See if you
can follow how things move from the Send button, to UpdateScreen(),
to PostCommand(), to the "SERVERDEMO1" section of ywaitdemo_exec.pl,
and finally how the data sent back from the server ends up as the
text displayed by the screen's JTextArea. Here's an brief outline
of what happens
Press Send
Java gets an ActionEvent from the button.
Java
Hands that ActionEvent to the Yoix interpreter.
Yoix
Translates the ActionEvent to its Yoix representation and then
calls the Yoix function named actionPerformed() that's defined
in the JButton and passes the Yoix version of the ActionEvent
to that function.
actionPerformed()
Calls UpdateScreen(), which is defined later in the screen. The
actual call is
root.UpdateScreen();
which uses an important field named root that's defined in the
Yoix representation Swing and AWT components and maintained by
the Yoix interpreter's layout machinery. The root field in our
JButton (or any other Swing component in the screen) points to
the JFrame that contains it, so that's where the UpdateScreen()
function is found.
UpdateScreen()
Calls QueueCommand() which eventually makes contact with the
server using PostCommand(). Arguments handed to QueueCommand()
are the screen (for the wait cursor), the name of the command
(i.e, SERVERDEMO1), the text currently in the JTextField that
has "$_argument" as its tag field, NULL (to mark the end of
the arguments sent to the server), and a pointer to the field
named text in the JTextArea that has "$_textarea" as its tag
field (data returned by the server ends up here). Take a look
at the JButton and JFrame reference pages for more information
about the tag and components fields.
QueueCommand()
Arranges a RunCommand() call with the same arguments, but that
call is made from a special thread named CommandThread, which
means QueueCommand() doesn't wait for the server to respond.
RunCommand()
Patiently waits for its chance to call PostCommand(), which
means RunCommand() doesn't return until the server responds.
PostCommand()
This is where the client and server exchange information. The
server side is handled by the ywaitdemo_exec.pl and if you look
for SERVERDEMO1 in that file you'll see where the data that's
returned actually comes from.
The PostCommand() that runs on the client sends everything up
to the NULL to the server, then waits for ywaitdemo_exec.pl to
return a header and additional data, splits that data up based
on information (e.g., SEPARATOR and ARGCOUNT) that the server
placed in the header, and then distributes the pieces of the
returned data to the remaining arguments. PostCommand() checks
each argument carefully and uses its type to determine what to
do with the returned data.
screens/ywaitdemo_template*.yx
These are supposed to be fairly simple screens that you can copy
and modify when you want to add a new screen to your application.
You can preview them by selecting "Examples->Template" from your
main screen's menubar. The template screens don't actually talk
to your server, but there are several other examples (discussed
below) that do.
admin/bin/broadcast.pl
Simple script that manages the broadcast messages that are sent to
users when they contact the server. The PINGTIMER variable defined
in your rc file gives you some over how often that happens.
admin/bin/cleanup.pl
A rather complicated script, usually run by cron, that can be used
to clean your system up. Suggestions that you get from crontab.pl
use this script.
admin/bin/crontab.pl
A simple script the suggests possible crontab entries that you can
use to maintain your system.
-----------------
Customizing YWAIT
-----------------
We're going to limit the discussion in this section to the most common
type of customization, namely hooking a new screen up to an existing
application. We'll show you how to build a simple screen file, arrange
to show it when the user does something (e.g., selects an item from a
menu), and exchange data with the server. We'll start working in the
YWAIT source, but we'll occasionally install our changes and test things
using our ywaitdemo application. We encourage you to follow along using
your source files and your application.
---
We assume you looked at the "Examples->Template" screens as you were
testing the system. We'll pick "Example 3" as the template for a new
screen that we'll call foo. The first thing to do is make a copy of
the template and somehow tell make about the new screen. We'll work
in the screens source directory, so we type
cd screens
to get to the right place and
cp ywait_screen_template3.yx ywait_foo.yx
to make our copy. Next edit the Makefile and change the line
SOURCE = \
to
SOURCE = \
ywait_foo.yx \
Actually you can add ywait_foo.yx anywhere in the SOURCE list as long
as you get the backslashes right. They're continuation characters and
we always add a space separator because old versions of make sometimes
misbehave without them. If you add ywait_foo.yx to the end of the list
append a space and backslash to the previous last line and omit them
on the ywait_foo.yx line. If you got things right typing
make install
should copy ywait_foo.yx to
/var/www/ywaitdemo/screens/ywaitdemo_foo.yx
but it's obviously not hooked up to anything yet, so there's nothing
else to test.
---
Next we'll add an entry in the main screen's menubar that will show our
new screen. It's not difficult, but might take a few tries if it's your
first time. The main screen is in the htdocs directory, so type
cd ../htdocs
to get to the right directory. You probably should save a copy, so type
cp ywait_main.yx ywait_main.yx.bak
before you make any changes. We're going to add an entry under "Show",
so edit ywait_main.yx and find the lines
new JMenu {
String text = "Show";
Array items = {
new JMenuItem {
String text = "Clipboard";
String command = "clipboard";
},
new JMenuItem {
String text = "Debugger";
String command = "debugger";
String tag = command;
},
new JMenuItem {
String text = "Logins";
String command = "logins";
},
new JMenuItem {
String text = "Messages";
String command = "messages";
},
new JMenuItem {
String text = "MOTD";
String command = "motd";
},
};
},
Add a new JMenuItem (the four lines line after the MOTD entry) to the
items array
new JMenu {
String text = "Show";
Array items = {
new JMenuItem {
String text = "Clipboard";
String command = "clipboard";
},
new JMenuItem {
String text = "Debugger";
String command = "debugger";
String tag = command;
},
new JMenuItem {
String text = "Logins";
String command = "logins";
},
new JMenuItem {
String text = "Messages";
String command = "messages";
},
new JMenuItem {
String text = "MOTD";
String command = "motd";
},
new JMenuItem { // added
String text = "FOO"; // added
String command = "foo"; // added
}, // added
};
},
that we'll eventually use to show our new screen. Incidentally, put
NULL,
or
"-",
between the "MOTD" and "FOO" entries in the items array and you end
up with a horizontal line drawn between the them in the menu.
We're not done with ywait_main.yx yet, but let's install the new version
anyway, and see what happens when we select "Show->FOO". Type
make install
and our modified main screen will be installed. Start your application
again and you should see an entry labeled "FOO" under "Show". Try it
and you should see an error dialog that displays the message
Missing action for command: foo
See if you can find where message is coming from.
---
Now let's work on showing our screen when "Show->FOO" is selected from
the main screen's menubar. Edit ywait_main.yx again, but this time look
for the lines,
case "screen_template1":
case "screen_template2":
case "screen_template3":
case "screen_template3a":
case "screen_template4":
case "screen_template4a":
Global.GetAndShowScreen(command, command);
break;
which are used to show the different template screens. Remember, our
screen is a copy of ywait_screen_template3.yx, so we'll initially show
it the same way, but we'll make our own Global.GetAndShowScreen() call
because the arguments will eventually change. Add the lines
case "foo":
Global.GetAndShowScreen(command, command);
break;
right after the ones that handle the template screens, and that should
do it. Save the changes, quit the editor, type
make install
and log back in to your application. This time our screen, which is a
duplicate of ywait_screen_template3.yx, should appear when "Show->FOO"
is selected.
---
In case you're wondering, we picked "foo" for the case label because
that's the value we assigned to the "command" field in the JMenuItem
that we added for our screen. It also happens to be part of the name
of our screen file (i.e., ywait_foo.yx) and Global.GetAndShowScreen()
uses its second argument, in this case "foo", when asks the server to
download a screen file. It's convenient, but not necessary. You could
change the "command" field in our JMenuItem to
String command = "this is a test";
and everything would work if you used
case "this is a test":
Global.GetAndShowScreen(command, "foo");
break;
to show our screen. We're still using command in the GetAndShowScreen()
call, but it's not required. GetAndShowScreen() uses its first argument
as an arbitrary name that YWAIT's screen handling machinery associates
with a particular screen. The first thing GetAndShowScreen() does is
look for an existing (i.e., cached) screen that's been linked to that
name, and if it finds one it doesn't bother building a new screen. In
other words, our screen is built the first time you select "Show->FOO"
from the menubar, but do it again (without dismissing the screen) and
it's not built again.
---
Let's make our screen look and behave differently than the one that we
copied. We're not going to do much, but a few simple changes will, be
instructive. First type
cd ../screens
to get us back to the right directory and then edit ywait_foo.yx. Look
for the lines
JFrame screen = {
Insets border = Global.GetLabeledScreenInsets();
String title = Global.ACRONYM + " Template 3";
Object parent = NULL;
Font font = Global.GetPreferredTextFont();
int opaque = TRUE;
//
// This is starting screen size. Setting size to NULL is special
// and means the individual components displayed in the screen
// determine the size.
//
Dimension size = {
double width = 7.0*72;
double height = 3.0*72;
};
change the screen's title and its size,
JFrame screen = {
Insets border = Global.GetLabeledScreenInsets();
String title = Global.ACRONYM + " Test Screen"; // changed
Object parent = NULL;
Font font = Global.GetPreferredTextFont();
int opaque = TRUE;
//
// This is starting screen size. Setting size to NULL is special
// and means the individual components displayed in the screen
// determine the size.
//
Dimension size = {
double width = 8.0*72; // changed: was 7.0*72
double height = 4.0*72; // changed: was 3.0*72
};
write it out, install the new screen file using
make install
login to your application, and test the new screen. You should notice
some differences when you compare it to template3. Dismiss our screen,
but don't quit the application, because we're going to make a few more
small changes. Find the lines
String screenname = name;
String screentitle = NULL;
Object anchorpoint = NULL;
int screenanchor = CENTER;
int initialized = FALSE;
int disposeonclose = TRUE;
int savestate = TRUE; // TRUE means remember size and location
int reloadargs = FALSE;
in your ywait_foo.yx source file, change screenanchor and savestate,
String screenname = name;
String screentitle = NULL;
Object anchorpoint = NULL;
int screenanchor = EAST; // changed: it was CENTER
int initialized = FALSE;
int disposeonclose = TRUE;
int savestate = FALSE; // changed: it was TRUE
int reloadargs = FALSE;
save the changes, quit the editor, type
make install
and try the screen. Nothing should change because the client remembers
the screen's Builder() function and won't go back to the server unless
we force it. Quitting is one way, but "Reset Screens" and "Reset All"
selections in the debugger screen are quicker. Select "Show->Debugger"
from the menubar and use the debugger to reset the screens (i.e., clear
the screen related data structures). Try the screen again and it should
behave differently.
The screenanchor change is immediately obvious. See if you can figure
out what savestate controls. Move the screen or resize it, dismiss it,
and then see what happens when you show the screen again. The behavior
should change when savestate changes.
---
The next thing we'll do is add two buttons to our screen, so point your
editor at ywait_foo.yx and look for the lines
Array layout = {
new JButton {
String text = "Send";
Font font = Global.GetPreferredButtonFont();
actionPerformed(e) {
//
// You undoubtedly have work to do here. All we
// currently do is arrange to call HandleSend()
// (it's defined below) from the command thread
// rather than from Java's event thread. It's a
// common approach when the work we're supposed
// to do could take a while (e.g., contacting the
// server).
//
Global.QueueCommand(root, &root.HandleSend);
}
},
new JButton {
String text = "Dismiss";
Font font = Global.GetPreferredButtonFont();
actionPerformed(e) {
Global.QueueCommand(root, &root.HideScreen);
}
},
};
which is where the buttons on our screen are currently defined. We'll
create a button labeled "Clear" and another one labeled "Test"
new JButton {
String text = "Clear";
actionPerformed(e) {
root.ClearScreen();
}
},
new JButton {
String text = "Test";
actionPerformed(e) {
root.components.$_textarea.text += date() + "\n";
}
},
and if we put this code between the two existing buttons that's where
the "Clear" and "Test" buttons will appear in our screen.
Save the changes, quit the editor, type
make install
and use "Reset Screens" in the debugger screen or log back in to your
application. Select "Show->FOO" and there should now be four buttons
near the bottom of your screen.
---
The last thing to do is exchange some data with our server. We'll keep
this simple and send you to the "Examples->Server" screens if you want
better examples.
The idea is that we pick an arbitrary new command name, say FOO, that
the screen can send to the server and that the Perl script
bin/ywait_exec._PL
that runs on the server will recognize. The screen uses QueueCommand()
or RunCommand() not only to send the new command and arguments to the
Perl script that runs on the server but also to handle the data that's
returned by the server. In other words, it's really just a matter of
coordinating the screen's Yoix code with the server's Perl script and
as long as they agree the data that's transferred between them can be
anything they want.
So we're going to use a command named FOO to ask the server for data.
Remember, we're going to keep this really simple because you can go to
the "Examples->Server" screens when you want better examples. All our
FOO command will do is return the server's current and we'll display
it in our JTextArea.
The first step is to edit the Perl script, so type,
cd ../bin
point your editor at ywait_exec._PL, look for the lines,
} elsif ($COMMAND eq "SERVERDEMO3") {
#
# This example expands on SERVERDEMO2b and shows how additional
# arguments (e.g., "-d" and "2") can be passed to the serverdemo
# script and used to control how the script handles the arguments
# stored in @args that came from the client. It's an example that
# illustrates how a script can handle related client requests.
#
serverdemo("-d", "2", @args);
append code that will handle our wonderful new FOO command,
} elsif ($COMMAND eq "FOO") {
WriteHeader();
print time() . "\n";
save your changes, and type
make install
to install the new Perl script. Nobody's hooked up to the FOO command
yet, but it's probably a good idea to test your application again just
to make sure Perl doesn't object to the code you added. If there's a
look in the httpd error_log for help.
---
Now let's make our screen use the new command. Type
cd ../screens
and edit ywait_foo.yx. First find the "Send" button definition
new JButton {
String text = "Send";
Font font = Global.GetPreferredButtonFont();
actionPerformed(e) {
//
// You undoubtedly have work to do here. All we
// currently do is arrange to call HandleSend()
// (it's defined below) from the command thread
// rather than from Java's event thread. It's a
// common approach when the work we're supposed
// to do could take a while (e.g., contacting the
// server).
//
Global.QueueCommand(root, &root.HandleSend);
}
},
and notice it calls QueueCommand(), but all that call does is arrange
to call a function named HandleSend() that's already defined in our
screen. Find the HandleSend() definition in our screen
HandleSend() {
//
// This function is eventually called when the user presses
// the Send button. All we do is pop up a message dialog, so
// you will have to change things. You probably will want to
// contact the server using Global.QueueCommand(), however
// in this case Global.RunCommand() may be more appropriate
// because we probably know we got here via QueueCommand()
// when the Send button was pressed.
//
Global.ShowMessage(this, "This is a message from HandleSend...");
HideScreen();
}
change it to,
HandleSend() {
Global.QueueCommand(this, "FOO", NULL, &components.$_textarea.text);
}
save your changes, quit the editor, and type
make install
to install the new screen file. Test your new screen, but don't forget
to use the debugger's "Reset Screens" is you're still logged in. Press
the "Send" button and the data that the server sent us ends up as the
only thing displayed in the JTextArea.
---
The behavior isn't as nice as our "Test" button, so lets see if we can
fix it. Edit ywait_foo.yx again, replace HandleSend() with
HandleSend() {
String arg;
Global.RunCommand(this, "FOO", NULL, &arg);
components.$_textarea.text += date(atoi(arg)) + "\n";
}
save your changes, quit the editor, and type
make install
to install the new screen file. Things now look much better - in fact
"Send" and "Test" now behave the same way.
Notice that we changed the QueueCommand() call to RunCommand(). Do you
can understand why? Here's a hint if you need one. The declaration
String arg;
creates a string variable named arg that starts out NULL, and the
atoi(arg);
call doesn't like a NULL argument.
-------
Summary
-------
That's it for now, but expect more in the near future.
Yoix is a registered trademark of AT&T Intellectual Property.
|