Defindit Docs and Howto Home

This page last modified: Jun 30 2004
title:Installing Flow LIMS

Tom's Installation Guide for Flow LIMS
(Tom is in the Academic Computing Health Sciences - ACHS at the
University of Virginia)

Flow LIMS (or just plain LIMS) is from  the Bioinformatics Facility at 
Fox Chase Cancer Center


"Flow LIMS includes software developed by the SAIC and the National Cancer Institute."

I installed Flow LIMS with a little help from my friends. I've
modified the original install docs from Fox Chase Cancer Center (FCCC)
to take into account issues I ran into.

I'm writing this from memory, so there may be typos.

This process will be easier if you do not have spaces or other odd
characters in your directory and file names.

Table of Contents
1.0. Assumptions and LIMS Login URL
1.1. Database Setup
2. Tomcat Setup
3. Archive Script Setup
4. LIMS Configuration
5. LDAP Setup and Config
6. Notes on iptables

1.0. Assumptions and Flow LIMS Login URL
Once you have gone through the extensive instructions below, you'll
want to access LIMS via a web browser.

Let's make the following assumptions for our examples:

Host name:
Installing userid: mst3k
User web directory: /home/mst3k/public_html
Flow LIMS directory: /home/mst3k/public_html/flow_lims

The LIMS Tomcat URL is:

The Flow LIMS documentation and other standard http accessible files
are at:

This assumes a fairly standard Apache setup. Userdirs are enabled for
public_html. Directory indexing is on. Apache is listening only on
port 80 (and NOT listening on 8080). Apache documentation is good, so
I won't go into details.

1.1. Database Setup

I'll cover Postgres since that's what I'm using. The original FCCC
docs include Oracle notes.

Just about every Postgres install needs changes to pg_hba.conf and
posgresql.conf (even if you are not running LIMS). On Redhat/Fedora
these files are found in /var/lib/pgsql/data. We have found it necessary to
create a postgres Linux account whose home directory is

Step 1. Allow LIMS to access the database without a password.
(attach example pg_hba.conf file). In my case I have several databases
on the same server, and there are more restrictive permissions for
the other databases.

su or login as postgres. 

Modify file pg_hba.conf. These lines are at the end of the file. 
local      all                                          trust
host       all    trust

Step 2. As far as I can tell, most technologies that access
Postgres use tcpip sockets. This is true for JDBC and Perl DBI. Modify
one line in postgresql.conf to read:

#       Connection Parameters
tcpip_socket = true

Step 3. su to root and start/restart Postgres.
/etc/rc.d/init.d/postgres start

Step 3. Go back to being logged in as yourself (a non-root,
non-postgres user, mst3k for our examples). Create a Postgres LIMS
user and database, and run the SQL for creating the LIMS db tables and
filling in some initial values ( lims-tables-postgres.sql ).

psql -U postgres template1
create user lims with createdb encrypted password 'a_good_password';
psql -U lims template1
create database lims;
\c lims;
\i lims-tables-postgres.sql

The \i command will generate a bunch of warnings about things that
don't exist and can't be deleted. That's fine.

2. Tomcat Setup (version 4.1.30)

Step 1. Set environment variables JAVA_HOME.  Edit your .bashrc and
add these lines where "mst3k" is your login. Make sure the path to
Java is correct. The path will vary depending on the exact version you
install. The same is true for Tomcat.

You need the Java SDK. The JRE is not sufficient. Tomcat refers to a
"JDK" or the "j2se", but there are no such things anymore. The J2SDK
comes as a .bin that turns into a .rpm. Use 

rpm -qpl rmpfilename.rpm

to find out where things are being installed. -q is query -p is
package (aka an rpm file) and -l is list. Pipe the results to less
since it is a huge listing.

rpm -qpl j2sdk-1_4_2_04-linux-i586.rpm  | less

My output looks like this:

I added these lines to my .bashrc file:

export JAVA_HOME=/usr/java/j2sdk1.4.2_04

It is theoretically possible to use a different sax xml parser.

Step 2. Download Tomcat. Create a directory outside your web
accessible directory tree, and gunzip/un-tar the Tomcat file.

-rw-r--r--   1 mst3k users   2199241 Mar 26 15:43 lims.war
-rw-------   1 mst3k users    372387 Mar 16 11:20
-rw-------   1 mst3k users    184576 Mar 16 11:18 pg73jdbc3.jar.112
-rwx--x--x   1 mst3k users  34363042 Mar 15 16:30 j2sdk-1_4_2_04-linux-i586-rpm.bin
-rw-------   1 mst3k users  30801920 Mar 15 15:15 jakarta-tomcat-4.1.30.tar
-rw-r--r--   1 mst3k users  34694346 Feb 24 11:09 j2sdk-1_4_2_04-linux-i586.rpm

I untar'd the jakarta tar file in my home directory, and followed
other instructions included with Tomcat in a file RUNNING.txt

Step 3. Edit .bashrc again.

If you un-tar Tomcat just as I did, add the following two lines to
your .bashrc (change the user id msd3k as appropriate):

export CATALINA_HOME=/home/mst3k/jakarta-tomcat-4.1.30
export CATALINA_OPTS=-Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXParser

Step 4. cd into your Jakarta (Tomcat) directory. Modify conf/server.xml for
Postgres as a datasource.
(attach server.xml)

There are a few paths to adjust for your userid, etc. As far as I can
tell, the paths are all absolute paths, and are not terminated with /.

More information about datasource setup in tomcat (versions 4.1) can be found in:

Step 5. Modifications to the conf/catalina.policy file.

At the end of the file add:

grant {

   permission "*:1024-65535", "connect,accept";
// cfg LDAP
   permission "ldap_ip_address:389", "connect";

  // Required for servlets and JSP's
  permission java.lang.RuntimePermission "";
  permission java.lang.RuntimePermission "*";
  permission java.lang.RuntimePermission "";
  permission java.lang.RuntimePermission "*";


If required, add permissions to access other folders used by the LIMS.

Step 6. Place jar files in the Tomcat common/lib folder.

If using PostgreSQL JDBC drivers for postgres7.3:


The original instructions meition jdbc2_0-stdext.jar, but this is
already part of Tomcat and is already in the common/lib folder.

Oreilly jar. Go to

Download any cos-*.zip (I think you only need the most recent one).

Unzip cos-*.zip

Find cos.jar and copy it into the tomcat common/lib folder.

Step 7. Place lims.war in the tomcat webapp folder. You need to unzip
lims.war. Some Java installations run .war files directly, but that
didn't work for me. Besides, you need to change one file inside

cd /home/mst3k/jakarta-tomcat-4.1.30/webapps
unzip lims.war

You should now have a LIMS directory full of files. Below are
instructions on creating/modifying a file lims/WEB-INF/web.xml.

Step 8. You may start Tomcat, but LIMS still isn't working.
cd into the Tomcat directory.

3. Archive Script Setup

Step 1. Assign the proper values to the following variables in

my $source = "path to shared export space"; (lab.exportDir in installation)
my $destination = "path to archive space"; (lab.importFCSDir in installation)

my $limstrace = "path to LIMS log file"; (for writing log information)

my $db_url = "URL for database";
my $db_username = "database user ID";
my $db_password = "password for database user";

Step 2. If using Oracle, change the following to reflect your system:


Step 3. Change the following variable in the subroutine 'callLimsUpdate' in

my $url = 'http://URL.for.your.webserver/
lims/ExpUpdateServlet?RequestType=update&expId=' . $keyword_;

Step 4. Setup to run as a scheduled process such as a crontab job. An execution frequency of 5 minutes should be sufficient.

4. LIMS Configuration

Step 1. Customizing web.xml.
See included file web.xml.

You can run the configure script to create the web.xml file in the
/config subfolder. The script is called "configure" and is in with the
LIMS HTML files.

However, the script isn't helpful, so I suggest editing the sample
web.xml file using my notes below. The file is not complex. The file
must be placed in your Tomcat webapps/lims/WEB-INF directory.

The configure script will prompt you for information about the
machines, folders, and users that the LIMS will use. The original
installation instructions say "Figure 1 shows some of these parameters
and the corresponding hardware compononents."  However, neither figure
in the original instructions matches this description.

For each parameter in the file, you want to focus on 2 lines at a
time: name and value. You only need to change the value for each of
the given parameter names. Directory names are full paths, not
terminated with a / (slash). I've entered examples for the user
mst3k. Normally the file is indented to make the parameters easier to
read. I'll do my example without the indentation. See the example
web.xml for a real file. I don't know if the parameter names are case
sensitive, but I assume they are. Certainly the Linux paths and file
names are case sensitive.

lab.ifs.conf is the ifs.conf file in the Tomcat conf directory.


lab.DiVa.conf is the diva.conf file in the Tomcat conf directory.


This is the ldap base dn as defined in slapd.conf and init.ldif. The
LDAP instructions below explain how to set and how to test this value.


This is th host ip address of your ldap server. I don't know if a
Fully Qualified Domain Name (FQDN) will suffice. Port 389 is probably
assumed but it doesn't hurt to be explicit. is the same
machine that is running Apache, Tomcat, etc.


I don't have a failover LDAP server. localhost is the same as, so this is kind of meaningless.


You don't need to change this. It is the ldap protocol name.


I think some of the flowjo files need to be web accessible. So I put
them in the user's public_html directory. You'll need to mkdir all
directories in this path.


The directory where xml for the DiVa import will reside.


Directory containing fascan data.


This is database configuration data, and is not web accessible, so I
put it in the Tomcat conf directory.


Directory where experimental data is stored. Web accessible, I guess.


Directory where experimental data (xml from DiVa) is stored


The LIMS log error file name including the path. Initially I didn't
fill this in and my installation still worked. I suggest using it.
I prefer that all files have extensions, so I use "error.log" instead
of the LIMS preference for "errorLog".


Info log.


Warning log.


This is the special user "operator".

The password for "operator". Yes, the password is in cleartext. This
is another reason to install Tomcat in a non-web accessible
location. I think the default password is "operator". For my test
installation I left this as the default, but I'll change it for the
production (real) installation of Flow LIMS. Incidently, I also use
iptables to restrict access to the LIMS site. (see iptables note


The special account "admin". This is the default. I didn't change it.


The password for "admin". I also didn't change this for my test
installation (same concept as "operator" above).


Default number of columsn on the plate.


For Postgres databases use this (the default).

      <description>DB Connection</description>

Step 2. Create folders for parameters you set in web.xml.

Step 3. Customize configuration files

lab.db.conf - contains settings for datasource
lab.ifs.conf - contains settings for file system storage area
lab.DiVa.conf - contains settings for creating diva xml file
lab.facscan.conf - contains configuration for facscan instrument

ifs.conf is the ftp url for accessing data files in the archive. I'm
not sure what this does, and I haven't configured mine yet. It looks
easy. This is the example. I'd normally expect a trailing / on any
URL/URI. I didn't configure this yet, and LIMS is running fine,
although I'm sure there is a feature that won't work without ftp.

ftp = ftp://user:password@host

Example diva.conf:

worksheets=/folder name where files containing plots settings are kept
Plots settings files consist of a part taken from DiVa generated xml file, 
the top part specifying plot settings.

instrSettings=/folder name where files containing instrumnent settings
are kept Instrument settings files consist of a part of DiVa generated
xml file, the part which specifies instrument settings.

sampleSettings=/file name containing gates settings for each
sample Sample settings file is a part of the DiVa generated xml file,
the part which is specifying the gates settings for the sample. Sample
identifier is removed from this part and is filled for each sample
with the appropriate LIMS sample name.

keywords=/file name containing keyword section to be placed in the xml
file Keyword file is a part of the DiVa generated xml file, which
allows the storing and passing of a keyword.

Example db.conf

This works for Postgres. You shouldn't need to modify this.

Example facscan.conf

<the channel number>:<list of allowed fluorochromes for this channel>

Step 4. Restart Tomcat. LIMS still isn't working because you need
LDAP. You should be able to get to the Login page, but you won't be
able to login yet.

5. LDAP Setup and Config

I'm using OpenLDAP which is part of the standard Fedora
installation. It is close to the build-from-source OpenLDAP
package. If you build from source you'll need at least one other
package. There is no uninstall for OpenLDAP built from source.

These instructions pertain to OpenLDAP and Fedora. File locations may
vary with other versions, but the concepts and file names should be
the same.

LDAP can be a huge nightmare, however, if things go well it is easy to

The LDAP setup instructions begin below at "Step 1." 

Before the instructions I've listed approximately a dozen LDAP
commands that worked for me. These are useful in tracking down
problems, and verifying that the system is working. You can run these
as a normal user. I only run them on my host. I've got iptables set to
block LDAP requests from other machines.

1. ldapsearch for namingContexts
The following ldapsearch should return your suffix as entered in
slapd.conf. However, I suspect it won't work until you've imported the
initial ldif file (see below).

ldapsearch -x -b '' -s base '(objectclass=*)' namingContexts

2. ldapsearch for everything related to a base.
This is handy. It returns everything for the -b base in your LDAP database.

ldapsearch -v -x -b 'dc=virginia,dc=edu' -s sub

3. Authenticated search.
This is the same as the namingContexts query above, but does simple
authentication as the Manager. You will be prompted "Enter LDAP
Password:" which means the Manager password. 

ldapsearch -b '' -s base '(objectclass=*)' namingContexts -x -W -D "cn=Manager,dc=virginia,dc=edu"

See the ldapadd notes below for more details on authentication.

4. ldapadd for a user.
This works when exmaple.mst3k.ldif is properly formatted, and probably
will NOT work until you have added example.init.ldif. As far as I
know, you could have several user records in a single ldif file.

Note that some example ldif files that you'll see in documentation on
the Web have the line:

objectClass: posixAccount

That line caused a descriptive error, and I commented out the
line. After that the ldapadd worked. 

Also note that ldapadd has funny behavior with regard to the
userPassword attribute. Apparently ldapadd hashes the userPassword. If
you give it something like:

userPassword: {MD5}wT2Iy0ywIAPa7bioTl0nKg==

what ends up in the LDAP database is something like this:

userPassword:: e01ENX13VDJJeTB5d0lBUGE3YmlvVGwwbktnPT0=

With all the things that were went wrong getting LDAP working,
I first thought this was some bizarre mistake. It is apparently
a feature.

I have tested ldapadd with clear text (non-encrypted, sometimes
written "cleartext"), {CRYPT}, and {MD5} passwords. All work fine. For
security reasons, avoid the clear text passwords. I'm guessing that
the hash is reversible, which would reveal your password.

If simple authentication doesn't work, first suspect your command line
args or your LDAP entry. For simple authentication as a particular
user you must have

-x -W -D <valid dn>

where <valid dn> is a valid distinguished name (dn, DN) from your LDAP
database. -x and -W are not order dependent. -D must be immediately
followed by the dn. I suggest testing authentication with ldapsearch.

-x is simple authentication
-W is prompt for password
-D is authenticate as this DN

Note that without -x LDAP will attempt SASL authentication. Also note
without -W the authentication will assume that you are trying to login
without a password, and that will fail too.

ldapadd -x -D "cn=Manager,dc=virginia,dc=edu" -W -f example.mst3k.ldif

5. ldapsearch authenticated as a user.
After reading in the ldif for mst3k, the following command worked. It authenticated as

ldapsearch -v -D 'uid=mst3k,cn=Mary,dc=virginia,dc=edu' -W -x -b 'dc=virginia,dc=edu' -s sub

6. ldapdelete.
This works. It does simple authentication, and deletes that Barbara J
Jensen example in the OpenLDAP documentation. For delete or modify to
work, the search field (the last arg on the following command line)
must exactly match the dn of the record. I get the impression that
every record in the LDAP directory must have a unique dn.

ldapdelete -v -x -D "cn=Manager,dc=virginia,dc=edu" -W "cn=Barbara J Jensen,dc=virginia,dc=edu"

deleting entry "dn:uid=mst5k,dc=virginia,dc=edu"
Delete Result: Invalid DN syntax (34)

7. ldapmodify
This also workd to modify a record. There are some clever commands
that can go inside an ldif file for ldapmodify, however, you can also
feed ldapmodify a changed file, and it updates any fields as necessary.

ldapmodify -x -W -D "cn=manager,dc=virginia,dc=edu" -f example.modify.ldif

If you get an error like:

ldap_modify: Server is unwilling to perform (53)
        additional info: referral missing

it probably means that you are asking to change something and one of
the fields in the modified file doesn't match the current contents of
the directory. Usually this means that your dn or one of the dn's in
your ldif aren't matching up. ldap doesn't say where the problem occured.

8. Non-authenticated search for a single user's info.

ldapsearch -v -x -b 'dc=virginia,dc=edu' -s sub uid=mst3k

9. Authenticated search for a single user's info.

ldapsearch -v -x -W -D "uid=mst3k,dc=virginia,dc=edu"  -b 'dc=virginia,dc=edu' -s sub uid=mst3k

10. You can get slapd into the attached-to-tty debug mode with the
following command (as opposed to a daemon which is not attached to a
tty). -d is debug level. I only got it to work with odd numbered
arguments that are a power of two -1. 1 and 255 are fine.

/usr/sbin/slapd -u ldap -h ldap:/// -d 1

(Note: LIMS does not use SASL. I didn't realize this when installing
LIMS, so I learned a few things about SASL. The following few commands
and their notes are not necessary for LIMS installation)

11. Search for supportedSASLMechanism. If you are doing SASL, this can be handy.

ldapsearch -x -s base -b "" supportedSASLMechanisms

12. SASL user listing. I don't understand the point of separate SASL
user lists, but this lists them (could be a shadow password thing). I
think there is sasldblistusers and sasldblistusers2. Again, I don't
know wny, and the docs didn't give any hints.


13. This is SASL authentication. You must add these users to the sasl
password db with the "saslpasswd2" utility (works just like passwd).
Note that -x and -U are not compatible arguments. -W doesn't seem
to have any effect.

ldapdelete -v -n -U -W "cn=Barbara J Jensen,dc=virginia,dc=edu"

14. This works fine, but requires the password that I entered via
saslpasswd2 (logged in as root), not the password in mst3k's LDAP entry.

ldapsearch -v -b 'dc=virginia,dc=edu' -s sub -U

15. This command failed because it wouldn't authenticate. I thought I
had this working at one point. If I wanted to fix it, I would start by
checking the sasl-regex in slapd.conf. I'd probably run slapd in debug
mode with -d 255. The second command with "cn=manager..." didn't work either.

ldapsearch -v -b 'dc=virginia,dc=edu' -s sub -D "uid=mst3k,dc=virginia,dc=edu"

ldapsearch -v -b 'dc=virginia,dc=edu' -s sub -D "cn=manager,dc=virginia,dc=edu"

The LDAP documentation is interesting. I never saw any documentation
that told what command to use to verify that some step was successful,
and LDAP commands often didn't have the expected effect. These web
sites might be useful. It takes a lot of reading between the lines of
to make sense of the documentation.

Step 1. Edit slapd.conf

See the enclosed file minimum_slapd.conf. The comments have been removed to
focus on the settings. The default slapd.conf has copious (and even
extraneous) comments.

su -l root
cd /etc/openldap
cp slapd.conf
emacs slapd.conf
(or edit with your favorite editor)

I won't bother putting the defaults here, only the things you need to
change relative to your hardware/software.

You need to include the schemas. This is the default.

LIMS uses ldap version 2 binding.  Allow that. This is not the default.

  allow bind_v2

The pidfile location is default.

The database type is ldpm. I don't know why, but it works.

  database	ldbm

I not sure what the requirements are for suffix, although you must be
consistent in how you use it. I just used the top two levels of my
domain name. It is ok to repeat attributes such as "dc". 

  suffix		"dc=virginia,dc=edu"

Root dn (distinguished name) should consist of "cn=Manager," and your

  rootdn		"cn=Manager,dc=virginia,dc=edu"

rootpw is the encrypted password.

  rootpw         "{MD5}pX4CtShLwtHNhJJcmVFvkQ=="

Use the command line utility

slappasswd -h {MD5}

which will prompt you for a password, the print the encrypted
string. Just paste the encrypted string along with the encryption type
into the rootpw field.

Directory is default.

All the index settings are default.

sasl-regexp is not necessary for LIMS because LIMS uses simple

  sasl-regexp uid=(.*),,cn=DIGEST-MD5,cn=auth uid=$1,dc=virginia,dc=edu

Regex one:

Regex two:

I didn't know that SASL was unnecessary. I'm pretty
sure that the only way to determine the format of these two regexp's
is to run slapd with debugging, and in attached mode (attached to the
tty, not as a daemon). When you try to authenticate, there will be
debugging info about what string it has as a DN. The first
regex must match the string that you see SASL trying to authenticate
against. The second regex must match your rootdn. The usual regex syntax
seems to apply. There's a note in the output to the effect: "Using
regex, resulting string is ..." You'll need your xterm/console set to
have thousands of lines of scrollback.

Step 2. Start the ldap daemon slapd.

/etc/rc.d/init.d/ldap start

Step 3. Import some base dn ldif info.

I'm not sure this is necessary. However, it worked, and LDAP says that
I can't delete this entry because other stuff depends on it. I entered
this command from memory, so I hope this is correct. Edit the
example.init.ldif to correspond to your hardware, slapd.conf, etc.

ldapadd -x -D "cn=Manager,dc=virginia,dc=edu" -W -f example.init.ldif

Step 4. Import one or more users.
The example is for the mythical mst3k. Edit for your users. One user
should be enough to test LIMS.

ldapadd -x -D "cn=Manager,dc=virginia,dc=edu" -W -f example.mst3k.ldif

I've included some example modify files. They are basically a user
file with changes. Different values will be updated. I couldn't find a
way to change the dn of a record, or the uid. The way to fix that
seems to be to delete the record.

Note that ldapdelete is different from ldapadd and ldapmodify. If you
use -f with ldapdelete, the file must consist only of one or more
valid dn's without (!!) the "dn:". ldapdelete apparently does not
understand comments. If you are only deleting one record it is best to
use a command line arg and forget the file. That's what is in the
ldapdelete example above. This would delete the mst3k from the example.mst3k.ldif:

ldapdelete -v -n -D  -W "uid=mst3k,dc=virginia,dc=edu"

Now you should be able to login to LIMS from a web browser on the LIMS
server. Some iptables modifications may be necessary to use the LIMS
web site from other computers.

6. Notes on iptables

If you get "connection refused" messages that often means that the
server is running and doesn't want to talk to you, as opposed to
iptables is blocking access to the server. (server being LDAP or Tomcat)

Modern iptables setups might give a "connection rejected" response,
but the usual recommendation is for iptables to 'drop' access instead
of 'reject'. Using 'drop' silently fails to connect. Attackers
can't tell why the machine is non-responding. 

As long as your LDAP connection is to your local machine (,
then the standard iptables will allow this connection. Your iptables
has a rule:

-A INPUT -i lo -j ACCEPT

which means accept any connection from the lo (loopback) address which
is which a special ip address meaning "myself".

LIMS and Tomcat use port 8080 which you'll probably have to open so
other machines can get to LIMS.

Test for iptables blocking your connections by briefly shutting
iptables off. As root:

/etc/rc.d/init.d/iptables stop

Try your LDAP search or LIMS web page request or whatever. If iptables
is the problem, open up the required port, and start iptables. I
recommend that you always make a copy of your iptables before editing

The following iptables line will open access to through on port 8080. This is a typical situation where you
want to allow access to LIMS from one subnet within your organization,
but not from the entire organization, and not from the entire internet.

-A input-chain-name -s -p tcp -m tcp --dport 8080 -m state --state NEW -j ACCEPT 


-A is "add to chain"

input-chain-name is the name of your chain. Get this from a line in
you iptables file that looks something like

:input-chain-name - [0:0]

-s is apply this rule to ip addresses through The /24 means that the address mask applies to the
left most significant 24 bits. That means 192.168.20 is significant,
and the last 8 bits are not checked (allowed in). A broader subnet
could be, and a single machine could be

-p tcp -m tcp probably apply to tcp protocol. I'm not sure about the meaning.

--dport 8080 means this rule applies to port 8080

--state NEW applies this rule to new connections.

-j ACCEPT is jump to accepting the connection (if all the criteria are

Below is a more standard RedHat rule, which I think accomplishes the
same thing. I'm not sure about the meaning of the --tcp-flags.

-A RH-Lokkit-0-50-INPUT -s -p tcp -m tcp --dport 8080 --tcp-flags SYN,RST,ACK SYN -j ACCEPT