Copyright (c) 1999-2008 Oracle Corporation, All rights reserved.
This module is a demonstration and example of the use of Oracle technology. It is made available for use at your own risk with no warranty of any kind. Please read the open-source license to make sure you understand the terms of use. The module has been approved for export from the USA; the ECCN is EAR99, NLR, with reference #01143.
| June 2001 | Last 8i-based release | Directory | ||
| November 2002 | Previous 9i-based release | Directory | ||
| windows_all.zip | Windows-format source, demos, tests, and binaries for WIN32 | Zip file for Windows | ||
| unix_all.tgz | Unix-format source, demos, tests, Linux binaries, makefiles | Gzipped tar file for Unix | ||
| owa.tgz | OWA PL/SQL scripts, Unix file format | Protected - not open-source | ||
| owa.zip | OWA PL/SQL scripts, Windows file format | Protected - not open-source |
mod_owa is an open-source implementation of the PL/SQL gateway available in Oracle's OAS/OWS product. The gateway is able to call PL/SQL procedures in a manner similar to the way Java servlets are supported, with the Oracle database acting as the "virtual machine". Arguments are marshalled through SQL*Net to your procedures, and responses returned the same way. HTTP header elements are made available via a CGI-like mechanism.
The original version of mod_owa was written by Alvydas Gelzinis (alvydas@kada.lt) and Oksana Kulikova (oksana@kada.lt), who still maintain their version at http://www.kada.lt/alv/apache/mod_owa. My version is a rewrite, adding functionality omitted by the original version, fixing several bugs, and adding some performance improvements. The motivation for this was to provide access to OWA-based content through the Apache web server at a time when Oracle lacked an Apache strategy. Edward Jiang (Edward.Jiang@oracle.com) contributed the file caching and range transfer support.
Oracle has now released its own Apache PL/SQL gateway module, modplsql. That module is professional-quality, well-tested, and officially supported. modplsql is 100% compliant with the gateway spec (including the NLS/multi-byte support and document upload/download) and supports additional functionality needed by Portal and HTML-DB. Though functionally similar, modplsql and mod_owa share no source code. modplsql and the scripts for the database (PL/SQL) side of the gateway are bundled with Oracle's new iAS product, as well as the Enterprise Edition of the database (as of 8.1.7). I encourage all users of mod_owa to take a look at iAS and modplsql, especially for production deployments. Please note that mod_owa will not support Portal and HTML-DB because of the specific modplsql features those Oracle products rely on.
It is assumed that you've already obtained and installed the OWA PL/SQL procedures supplied by Oracle with OAS, iAS, Portal, and several other products. Sorry, but that code belongs to Oracle and has not been approved for open-source license so I can't make it available here. You can, of course, implement your own OWA work-alike. An incomplete sample of one is included in the test scripts, in file modowa.sql.
The module requires an Oracle client installation to run. Specificially, it requires a so-called ORACLE_HOME area to be installed, complete with the Required Support Files (RSF) including the OCI client library.
Although 2.0 and 2.2 versions are available, this documentation is oriented towards the original Apache 1.3 version of the module. The code was designed for Oracle 8.1.x, and is currently built with Oracle 10.0; it contains calls that won't be satisfied by 8.0.x versions of Oracle's client environment. The code is #ifdef'ed such that if necessary you can rebuild it easily enough in an 8.0.x environment. Since the code is written in OCI8, you can't run it in a 7.x client environment; however, from an 8.x environment, it should run correctly against a 7.x data server, naturally without supporting LOBs and other 8.x features, though. (Note: there is a backport from OCI to the older UPI, please look for that sub-section in the release information section of this document.)
If you have problems or questions, please feel free to send me e-mail at Doug.McMahon@oracle.com . I'd recommend checking this site periodically to see if there's an update to the code.
Note: this version of mod_owa breaks backward compatibility with some older versions. It has two changes that are not compatible (by default) with older versions. In both cases, use of directives can make mod_owa behave in a compatible manner.
- Admin functions restricted by default
In older releases, mod_owa had the administrative/control functions enabled by default, requiring the use of the OwaAdmin directive to explicitly disable or restrict them. This is now reversed; the functions are disabled by default, requiring the use of OwaAdmin to explicitly enable/restrict them. I expect little problem from this; few people are likely to be using this feature, and those that are most likely are already setting OwaAdmin.- Query string arguments for POST requests
The original Gelzinis/Kulikova code folded both GET and POST requests down to a single code path for procedure argument processing. The argument string for GET was taken from the URL's query string, while for POST it was taken from the request body. I continued this in the initial rewrite, and it's been that way ever since. Unfortunately, it's wrong; POST requests can also have URL query string arguments. I've now fixed this by including any URL query string arguments with the request body arguments - they're processed first, just ahead of the request body (in case argument order matters to your code). I decided I did not want mod_owa to have the incorrect behavior by default, so I've made this the default. It can be disabled, and the old incorrect behavior restored, by using the new NOMERGE argument to the OwaAlternate directive:
OwaAlternate OWA NOMERGE
mod_owa is packed in two separate bundles, windows_all.zip for the Windows platform, and unix_all.tgz for Linux and other Unix platforms. Both bundles have files in a similar directory structure, described below. Note that any ASCII-based file (e.g. source files and scripts) will be in the carriage-control format native to the platform (i.e. with newline separators for Unixes and carriage-return + line-feed for Windows).
| bin | Binaries: object files built from the source and the dynamic library for the platform in question. The Unix binaries are all Linux/X86. A make file for the platform is also found here. | |
| src | C source code for mod_owa. | |
| doc | Documentation files, including the license. | |
| apache2 | Apache 2.0 binary and make file. | |
| apache22 | Apache 2.2 binary and make file. | |
| classes | Binary class files for the servlet implemenation (very out of date). | |
| java | Java source files for the servlet implemenation (very out of date). | |
| modtest | Source and binaries for the simple mod_test module. | |
| owad | Source and binaries for the stand-alone owad server. | |
| test | SQL scripts and test pages for the tests and demos. | |
| makefiles | For different Unix platforms (unix version only). |
| Unpacking | unzip windows_all.zip | |
| Building | If necessary, you can rebuild from source provided you have the Oracle, Apache, and VC++ headers and link libraries. The modowa.mak file has these located in places particular to my development machine, so you will probably have to edit the file for your environment. After this, nmake modowa.mak. | |
| Installing | Copy mod_owa.dll to <apache_home>/modules. To configure, add to httpd.conf: AddModule modowa.c Location directives as described below The AddModule directive may or may not be needed. It should be implicit in the LoadModule; however, if you've used the ClearModuleList directive, you may need to reactivate mod_owa with the AddModule command shown. Apache 1.3 will give a warning if AddModule was unnecessary, but will otherwise start up correctly. Note that the pre-built binaries are built using Visual C++ 6.0 on Windows XP with Oracle 10.0. I have found that the 1.3 version seems to be portable to older versions of Apache back to 1.3.6, and to older versions of Oracle 8.1 (and, by recompiling, to 8.0). | |
| Running |
Restart Apache. Note that the Oracle environment will be assumed to be set in the context of the user starting the web listener and/or from the registry. It is important that ORACLE_HOME be set because the module must know where the Oracle client environment is located. If you are serving from a local database, ORACLE_SID should also be set. Other Oracle context is taken from the environment and/or registry as well, including important NLS settings such as NLS_LANG. |
Note: the name of the module has been changed from ApacheModuleOwa.dll to mod_owa.dll. You can rename the file if you like; otherwise, be sure your configuration file's LoadModule command matches the new name.
| Unpacking | tar xfz unix_all.tgz | |
| Building | If necessary, you can rebuild from source provided you have the
Oracle and Apache headers and the Oracle link libraries. The modowa.mk
file was written for Linux and has these located in places particular to
my development machine, so you will probably have to edit the file for
your environment. After this,
make -kf modowa.mk.
| |
| Installing | Copy mod_owa.so to <apache_home>/modules (or libexec). To configure, add to httpd.conf: AddModule modowa.c Location directives as described below (The module file name is mod_owa.sl on HP/UX.) The AddModule directive may or may not be needed. It should be implicit in the LoadModule; however, if you've used the ClearModuleList directive, you may need to reactivate mod_owa with the AddModule command shown. Apache 1.3 will give a warning if AddModule was unnecessary, but will otherwise start up correctly. Note that the pre-built binaries are build on Fedora Core 6
(2.6 kernel) for Linux x86, using Oracle 10.0 libraries. Unfortunately,
Linux is fussy about the version numbers on the OCI library, libclntsh.so,
so you will probably have to relink if you're using another database
version.
| |
| Running |
Restart Apache. Note that the Oracle environment will be assumed to be set in the context of the user starting the web listener. It is important that ORACLE_HOME be set because the module must know where the Oracle client environment is located. Also, since the module is linked dynamically you must ensure that the Oracle libclntsh.so you linked with is on your LD_LIBRARY_PATH. If you are serving from a local database and not using SQL*Net, ORACLE_SID should also be set. Other Oracle context is taken from the environment as well, including important NLS settings such as NLS_LANG. |
As of this latest release, I will no longer be supplying binaries for HP/UX, AIX, or Solaris. The main reason for this is that I no longer have access to machines and environments where I can do the builds. Users that need support for these should rebuild the code from source using the makefiles supplied in the unix_all.tgz.
Special note: posix threads, httpd, and libclntsh.so
After upgrading to 8.1.6 and to RedHat 6.1, I discovered that mod_owa.so would no longer load into httpd. The httpd simply fails to start, without issuing any useful error message. I believe this problem is due to the fact that Oracle's libclntsh.so now depends on the posix threads library, libpthread.so, which must be linked with the main executable, httpd, with the flag "-lpthread". If this is not done, then when httpd attempts to load mod_owa.so, and hence libclntsh.so, the loader will error out because it can't do a dynamic load (via the dlopen() system call) of libpthread.so. Replacing the shipped version with a rebuilt listener solved the problem; I edited the Makefile produced by Apache's "configure" script to make sure LDFLAGS1 is set to "-lpthread". A quick way to check this is to use the "ldd" command to see what bindings your httpd is built with - make sure libpthread.so is one of them!
If you suspect you have this problem, I've included a very simple module, mod_test.so, which does little more than initialize the OCI (and, by omitting the "-DWITH_OCI" compile flag and the "-lclntsh" link flag, not even that much). If you can't get mod_test.so to load, try building it without the OCI dependency, and see if it loads. If it does, you have this problem. If it still doesn't load, then you have some other incompatibility between the build environment you're using to build mod_test.so and the build environment that produced your httpd. Either way, the only answer I have is to rebuild Apache from source to see if that clears the problem.
Oracle ran into this problem with the first version of iAS, and we are now going to ship the httpd that comes with iAS built with -lpthread on all platforms.
HP/UX may also need to have libcl linked into httpd (e.g. "-lcl").
Older versions of Oracle on Linux (e.g. 8.0.x) did not seem to have a dependency on libpthread. mod_owa was originally developed on 8.0.x, and so I didn't encounter this problem, or even understand it, until after the upgrade to 8i.
If you need to rebuild mod_owa for any reason, you should be aware of the following compilation flags:
| WIN32 | Compiles the code for Windows NT/2K/XP. | |
| UNIX | Compiles the code for Unix versions. | |
| EAPI | Compiles the code for EAPI extensions (used by mod_ssl). | |
| EAPI_ONLY | Compiles the code for EAPI extensions using the new EAPI module magic numbers (the resulting module won't load in non-EAPI Apaches). | |
| OLD_ORACLE | Disables 8.1.x functions, compiling for 8.0.x. | |
| ORACLE7 | Compiles UPI-based code for Oracle 7.3.x clients, also forces the definition of OLD_ORACLE. LOBs are not supported. | |
| NO_FILE_CACHE | Disables the file caching code (the file caching code is not entirely portable). | |
| NO_THREADS | Disables the connection-pool cleanup code (which is not entirely portable). | |
| NO_SOCKETS | Disables network/socket related code (which is not entirely portable). | |
| APACHE20 | Builds the Apache 2.0 linkage; the resulting module will run under Apache 2.0 but not under 1.3.x. | |
| APACHE22 | Builds the Apache 2.2 linkage; the resulting module will run only under Apache 2.2. |
The source files are:
| modowa.h | Header for everything | |
| modowa.c | Main linkage to Apache | |
| owahand.c | Core of the request handler | |
| owaplsql.c | Basic PL/SQL linkage | |
| owadoc.c | Document upload/download | |
| owacache.c | File-system and shared-memory cache | |
| owasql.c | OCI-based functions | |
| owanls.c | Oracle-related character set functions | |
| owautil.c | String functions and misc. utilities | |
| owafile.c | OS-dependent I/O routines |
A few very simple test procedures are supplied with mod_owa, in the form of some HTML test forms and SQL scripts to create database test objects:
| modowa.sql | Creates minimal equivalent of OWA server-side. Creates some simple packages that mimic the functionality of the OWA sufficiently for mod_owa to make requests and get page responses. | |
| tables.sql | Creates document storage tables used by demodoc.sql. Creates user table needed by demoauth.sql. Also creates the OWA_ARRAY object type needed by Java. | |
| owatest.sql | Very basic test procedures for use with owatest.htm. | |
| demodoc.sql | A demonstration package for document storage and retrieval. | |
| demoalt.sql | A demonstration/test package for using an OWA alternative. | |
| demoauth.sql | A demonstration/test package for Basic authentication. | |
| owatest.htm | Test form to invoke procedures in owatest.sql. | |
| owajtest.htm | Version of owatest.htm that uses the servlet implementation. |
Please note that the scripts contain references to SQL object types, LOBs, and NCHARs, which you might have to remove to get them to run against an older database server.
In response to some requests, I've added code to enable mod_owa to be compiled and linked against Oracle 7.3 client stacks. This code uses Oracle's UPI, which unfortunately is not published in any of the standard header files (it's an internal interface requiring internal headers). The Oracle 7.3 version does not support LOBs. In addition, you lose the ability to specify DAD character sets different from the global character set of NLS_LANG, because the pre-8.0 UPI lacked the ability to override the default character set on a per-bind level. Finally, you lose the ability to specify NCHAR binding modes, again because the pre-8.0 UPI lacked this capability.
The code can also be built against the 8.0 version of the OCI, which had a few annoying differences versus the 8.1 version. All subsequent versions of the OCI are compatible back to 8.1, so 8.0 must be regarded as a special case. The 8.0 version lacks a few APIs available in 8.1 to set up the OCI environment, and moreover requires that a global variable be defined to satisfy an unused but linked-in dependency in libclntsh.so.
I am no longer able to provide builds against these older versions of the OCI because I simply don't have environments for them. However, if necessary I can build versions using those flags against my 10.0 or 11.0 environments, and they should work against older client environments. Those backports are available on request.
I suggest starting by reading the information in this document carefully. There's a good chance your problem is covered somewhere already, and even the contents of this section assume you already know concepts discussed later in this document. That said, here are some common errors with mod_owa installations:
OWA_CUSTOM.AUTHORIZE
Oracle ships the OWA_CUSTOM (and, in older OWAs, the OWA_INIT) package with a default implementation of AUTHORIZE that always returns false. If you've set OwaAuth in httpd.conf, mod_owa will call this routine and it will effectively disable any calls to your procedures. The symptom is usually an HTTP 404 error (URL not found). Remove the OwaAuth directive or change AUTHORIZE to return true if you don't want security. Otherwise, be sure to replace AUTHORIZE with a correct security check.
Flexible arguments
The old OAS apparently had a feature whereby it would call a procedure with the "flexible argument mode" despite the lack of the required "!" prefix on the URL. It had some sort of heuristic where it was able to figure out that this should be done, probably after a call built with the signature set by the OWA specification silently failed. mod_owa doesn't have this feature, and it can't be added because there is no foolproof way to figure out that this is required (I can think of several ambiguous cases), and because the architecture of mod_owa simply can't support it. The usual symptom of this problem is a 400 ("bad request") error (or a SQL error page if the diagnostic is enabled). A quick fix is to specify such procedures with OwaFlex directives, though it would be better to use the specified "!" prefix, or code overloads of the procedure in question to match the call signatures actually expected.
OWA gives PL/SQL errors with multi-byte databases
This is due to an incorrect implementation of the page buffer in the HTF package (pubht.sql and privht.sql). The problem arises because SQL and PL/SQL data buffers measure their maximum lengths in bytes, whereas all string operations, e.g. SUBSTR, LENGTH, etc., measure in characters. As a result, the HTBUF_LEN limit of 255 is not enforced properly with multi-byte character sets. I am trying to get Oracle to fix this by using the byte-based equivalents SUBSTRB, LENGTHB, etc. in the underlying code. Meanwhile, the simple fix is to modify the code of pubht.sql and reduce this limit based on your worst-case character size. For most character sets, this will be 2 bytes per character, so the limit would be 127. For UTF8 Unicode, it's 3 bytes per character, meaning the limit should be 85. For the newer AL32UTF8 Unicode, it's 4 bytes per character, and the limit should be 63. For a small number of Asian character sets it's also 4 bytes per character (you can recognize them because they will have a "32" in the Oracle character set name, e.g. "ZHT32EUC"), and the limit should be 63.
2000-byte CGI value limit in pubowa.sql
In older versions of the OWA, the tables that hold CGI name/value pairs in PL/SQL were limited to 2000 bytes. When passing the CGI values to PL/SQL, mod_owa truncates large values to the maximum size allowed by the OCI, 3999 bytes. It is therefore possible, with an older OWA, to get a PL/SQL "value or numeric" error from a string buffer overflow. The simplest solution is to change the definition of vc_arr in pubowa.sql and increase the limit to 4000, matching more modern versions of OWA. I believe this will also work on older databases since PL/SQL has always had a 32512 byte maximum for string size. However, it's possible that the network code won't support the 4K maximum. If you believe you have such a problem, try using one of the backport versions of mod_owa (for example, the Oracle 8.0 version), since these impose the lower 2K limit.
Database crashes passing Basic authentication
OWA_SEC functions don't work
Users moving from OWS may find that their user authentication code doesn't work without some modification, because fundamentally mod_owa is an Apache driver for the OWA, not a replacement for OWS. In particular, the functions in OWA_SEC are not fully supported. The function OWA_SEC.SET_AUTHORIZATION_SCHEME is irrelevant; mod_owa passes all requests to your PL/SQL code and expects you to perform any necessary authentication operations. If you are using Basic authentication, you need to arrange for a challenge to be sent to browsers that haven't logged into your site; this can be done from your AUTHORIZE function or directly from each procedure, under conditions you determine. Apache will pass any Basic authentication information back to you in the HTTP headers. Please review the section on Basic authentication later in this document.
Characters garbled or incorrectly converted
This is most likely due to a misconfiguration or misunderstanding of how character sets are handled between the browser and Apache, and/or between mod_owa and the database. The quick checklist is to verify proper setting of the NLS_LANG environment variable in the environment used to launch Apache, verify proper setting of the OwaCharset (if it's needed to override NLS_LANG), verify proper setting of the browser character set, and verify the database character set by querying the value of parameter 'NLS_CHARACTERSET' from V$NLS_PARAMETERS using sqlplus. If none of this helps, read the section on character set handling later in this document.
httpd won't load mod_owa on Unix
The most common reason for this is that httpd wasn't linked with -lpthread, which is now required by Oracle's OCI. Please review the release notes above for Unix systems. On Linux, the symptom is that httpd appears to exit with no error message.
Apache unable to load mod_owa on Windows
The most common reasons for this are incorrect placement of the mod_owa.dll file versus the LoadModule directive, or a missing or incorrect version of the Oracle libraries (e.g. OCI). Make sure the path in LoadModule corresponds to the path that Apache is displaying in the error message and that this path is valid. If the error mentions that "the specified procedure could not be found" this may mean that the correct OCI library cannot be located. At least one user with multiple Oracle homes on the same Windows system encountered this problem because his 8.0.x OCI library was found first, ahead of the 8.1.x version. Note that setting ORACLE_HOME will not change the order in which Windows will search directories for dynamic libraries. Either make a global change to the path or use the 8.0.6 backport version of the mod_owa.dll library.
Apache 1.3 gives warnings/errors loading mod_owa
There are unfortunately two variants of Apache 1.3, the standard version, and the SSL version. The SSL version has the so-called EAPI module extensions. To signal modules that were compiled with the EAPI extensions, there is a new magic number that gets compiled into the code. The SSL version will load a module that it thinks is compiled the old way, but will give you a warning because the end of the module structure may be missing some necessary pointer structures, and may cause a crash. The standard version will not load an EAPI module, however; it will give you an error message claiming the module is not an Apache module. mod_owa can be compiled in several ways; it can be compiled without EAPI, it can be compiled with the EAPI extensions but using the old magic number, and it can be compiled exclusively for EAPI. By default, on Linux and Windows I build it with the EAPI extensions (thus avoiding the possibility of a crash) but using the old magic number (thus allowing it to work fine in the standard Apache). If you have an Apache with mod_ssl and you need to get rid of the warning message, rebuild the code using the EAPI and EAPI_ONLY flags.
Silent failures during RESET_PACKAGE
After processing an HTTP request by calling your PL/SQL code, and getting/relaying the results from GET_PAGE, mod_owa must call the RESET_PACKAGE procedure to clear out the PL/SQL state for the next request. This is the only SQL operation performed by mod_owa after it sends the results to the browser; because of this, any error that occurs at this point can't be displayed to the browser (even with the SQL diagnostic enabled). However, with the SQL diagnostic, mod_owa always writes the final SQL status to the mod_owa log file, so it should be possible to see if failures have/are occuring by consulting that file. Failures at this point are very unlikely, but at least one user has encountered a failure here due to a mis-configuration (using the OwaReset directive to cause mod_owa to call one of the newer reset modes against a database that did not support them).
Oracle environment variables
It's very important that ORACLE_HOME be set to point to the location where your Oracle software is installed. It's also critical that the directory containing libclntsh.so be on your LD_LIBRARY_PATH. Other important environment variables to look at are NLS_LANG (if you are trying to run a particular locale) and ORACLE_SID (if you are not using SQL*Net to connect to your database). A good way to check the Oracle setup is to run sqlplus from the environment where you launch httpd and connect to the database using the identical connect string specified in httpd.conf for mod_owa. Note that on Unix it's vital that areas of the ORACLE_HOME be readable by the Apache "run-as" user; some users have run into problems because areas of their ORACLE_HOME were not readable by the "nobody" user they set Apache up to run as (check the User and Group directives in httpd.conf). A very common symptom of this class of configuration problem is to get an -1 error on OCI connect.
SELinux issues
It's important to make sure Oracle's OCI library and associated files in ORACLE_HOME are readable by the Apache worker processes. Many users running SELinux run into problems because they forget to do this. A common symptom is to get file-system privilege errors when attempting to start Apache, often when loading libclntsh.so (the very first Oracle-supplied file to be accessed).
OwaLog diagnostic file is not written
Database connection exhaustion with Apache 2
Apache 2.0 and beyond have all but desupported child/worker-process cleanup. Thus, there is no opportunity for mod_owa to close any pooled database connections when a worker goes down. Unfortunately, this doesn't just mean that you might lose connections when a crash occurs, because it can happen if the Apache manager process decides to shut down a worker as load subsides on the system. Connection loss thus appears to be a fact of life now with Apache. Either disable pooling completely (using a pool size of 0, or via the THREADS diagnostic flag), or ensure that your database server checks for and kills inactive database processes (the server side of the dead connections).
HP/UX issues
The biggest single problem is that Oracle's libclntsh.sl, on which mod_owa depends, was shipped with some undefined symbols. The HP/UX loader will fail to start Apache but typically gives misleading error messages about the cause. Be sure that you've got the patched version of this library from Oracle before trying to use mod_owa. Another common source of trouble on HP/UX is to mix an incompatible combination of 32-bit and 64-bit executables and shared libraries. Use the HP/UX-specific "chatr" function to check httpd, mod_owa.sl, libclntsh.sl, and other key libraries. Also, make sure httpd was linked with libpthread and libcl (chatr will tell you this, too). You can add -lcl and -lpthread to the Makefile that relinks httpd and the relinked version will work.
The main task involves setting up "locations" that point into the OWA module from within httpd.conf. For example:
<Location /owa>
AllowOverride None
Options None
SetHandler owa_handler
OwaUserid owa/owa
OwaNLS WE8ISO8859P1
OwaAuth OWA_INIT
OwaDiag COMMAND ARGS CGIENV POOL SQL MEMORY
OwaLog "/usr/local/apache/logs/mod_owa.log"
OwaPool 20
OwaStart "doc_pkg.homepage"
OwaDocProc "doc_pkg.readfile"
OwaDocPath docs
OwaUploadMax 10M
OwaCharset "iso-8859-1"
order deny,allow
allow from all
</Location>
Each location can be thought of as "mounting" a distinct database
and database context. In the above example, URLs will be directed into the
PL/SQL gateway when they have the form:
http://mymachine.../owa/procedurename...
With locations, you can set up separate "mounts" for different
databases, or multiple "mounts" to the same database with different
NLS language settings, etc. Use of patterns (LocationMatch directives, e.g.
"/owa/[^/]*") is supported (see the later section on
LocationMatch support). Use of structured locations e.g.
"/owa/subdir", is supported with special-case code. Use of
"/" as a location (directing all web traffic to mod_owa) is
also supported with special-case code. The mod_owa-specific directives
in this Location example are described in next section.
A common practice is to place all the OWA-related Locations and other directives, including the LoadModule directive, in a separate .conf file (e.g. modowa.conf) and then include this file into httpd.conf with something like:
include modowa.conf
mod_owa has a number of directives that are intended for use within a Location directive and that establish settings solely for that Location. There are also two optional directives that appear outside the scope of any location to specify global (Apache-wide) settings.
When I started working on mod_owa, I was initially just building extensions to the original version, and I followed the naming convention established by the original authors. Later, after the rewrite, I found myself adding a great many new directives, but felt obliged to continue using this naming convention. In the latest versions of mod_owa, I've switched to a standards-conforming naming convention where every directive is prefixed with "Owa". The old names are still supported for backward compatibility, but will be dropped as of the Apache 2.0 release. Users are encouraged to adopt the new names in their .conf files.
Summary of directives:
| Per-Location Directives | ||||
| 2.0 Directive | 1.3 Equivalent | Short Description | ||
| OwaUserid | oracle_userid | database connect string | ||
| OwaNLS | oracle_nls | language/territory (& OCI char set) | ||
| OwaAuth | oracle_ver | choose AUTHORIZE function | ||
| OwaDiag | oracle_diag | diagnostic flags | ||
| OwaLog | oracle_log | diagnostic logging file | ||
| OwaDescribe | oracle_describe | describe mode | ||
| OwaPool | oracle_pool | size of connection pool | ||
| OwaStart | oracle_start | procedure for default start page | ||
| OwaBefore | oracle_before | procedure to run before all procedures | ||
| OwaAfter | oracle_after | procedure to run after all procedures | ||
| OwaProc | oracle_proc | site-wide procedure for all calls | ||
| OwaRealm | oracle_realm | use database login for Basic authentication | ||
| OwaAlternate | oracle_alt | name of alternate OWA implementation | ||
| OwaUnicode | oracle_uni | enable NCHAR/NVARCHAR binding modes | ||
| OwaReset | oracle_rset | change RESET_PACKAGE mode | ||
| OwaAdmin | oracle_admin | restrict admin functions to IP addresses | ||
| OwaRound | oracle_round | round bind variable sizes | ||
| OwaSqlError | oracle_error | URL for showing SQL errors | ||
| OwaSession | oracle_ses | cookie name for session/connection binding | ||
| OwaDocProc | document_proc | document-read procedure | ||
| OwaDocPath | document_path | document-download prefix (LOBs) | ||
| OwaDocLong | document_long | document-download prefix (legacy) | ||
| OwaDocFile | document_file | document storage directory | ||
| OwaDocGen | document_gen | dynamic document prefix | ||
| OwaDocLobs | document_lobs | control document LOB bindings | ||
| OwaDocTable | document_table | enable WebDB document upload/download | ||
| OwaUploadMax | upload_max | maximum size of content upload | ||
| OwaCharset | dad_charset | IANA character set for location | ||
| OwaBindset | dad_bindset | Oracle character set for non-UTF8 requests | ||
| OwaDateFmt | dad_datefmt | format mask for dates and timestamps | ||
| OwaTZ | dad_tz | time zone for location | ||
| OwaCache | oracle_cache | caching directory and location | ||
| OwaFlex | oracle_flex | use flexible arguments for procedure | ||
| OwaReject | oracle_reject | disallow prefixes | ||
| OwaEnv | oracle_env | add name/value pairs to environment | ||
| OwaLDAP | oracle_ldap | convert basic auth user/pass to DB user/pass | ||
| Global Directives | ||||
| 2.0 Directive | 1.3 Equivalent | Short Description | ||
| OwaSharedMemory | owa_shared_memory | size of shared memory segment | ||
| OwaSharedThread | owa_shared_thread | cleanup thread polling interval | ||
| OwaFileRoot | n/a | file system root for static content (works for owad only) | ||
These directives are described in more detail in the table below. In each case the new (2.0) directive name is given first, and the old directive name is given second. Very complex directives have additional information provided in later sections of this document.
| Directive | Description | |
| OwaUserid oracle_userid | username/password@database for the data server to run the PL/SQL requests. This parameter is required. Note that the database name is a SQL*Net V2 locator. If omitted, the connection will be through the ORACLE_SID taken from the startup environment. | |
| OwaNLS oracle_nls | An Oracle NLS_LANG setting of the form | |
| OwaAuth oracle_ver | The way in which OWA handles authorization callbacks has changed in
a recent release. Previously, it would call OWA_INIT.AUTHORIZE and
then execute your request on a TRUE return. Newer versions call
OWA_CUSTOM.AUTHORIZE. This optional parameter lets you control
which of these to call. It should be set to "OWA_INIT" or
to "OWA_CUSTOM", as appropriate. Make sure it matches your
actual deployment within the database! If you omit this parameter, then
no check is made, all procedure calls are just executed directly. You
can also add the extra keyword PACKAGE, as in | |
| OwaDiag oracle_diag | This is a set of flags, described below, that enable various diagnostic modes built into mod_owa. The parameter is optional. Note that if you set this you might create a security issue, since some of the diagnostics show the actual SQL on error conditions. | |
| OwaLog oracle_log | A pathname to a file where mod_owa diagnostics should be written. It's best to specify a full path. I use "/usr/local/apache/logs/mod_owa.log" on Linux, and "D:\apps\apache\logs\mod_owa.log" on Windows. This parameter is optional; if not specified, the default is "mod_owa.log" (but note that it's only written to if you turn on one or more of the diagnostics). | |
| OwaDescribe oracle_describe | This optional parameter allow you to specify how mod_owa handles argument-bind failures. It consists of a mode parameter and/or a schema name. The allowable mode values are STRICT, NORMAL, and RELAXED. NORMAL is the default and is compatible with previous versions of mod_owa. The operation of this parameter is described in a later section. | |
| OwaPool oracle_pool | This governs the size of the connection pool for the module. Each Location has its own pool, so all connections are identical within a pool (e.g. they all point to the same underlying Oracle schema and they all have the same NLS settings). On Unix this parameter has no effect (unless you set it to 0); because the Unix version of Apache isn't multi-threaded, the pool will always have at most one connection. The parameter is not required, and if not specified the default is either 1 (Unix) or 10 (Windows). The maximum pool size you can specify is 255. Note that if a request is processed and the pool is exhausted, a connection will be created and destroyed to service that request, rather than blocking on the availability of a connection from the pool. The pool is initially empty and is filled up to the maximum size only by user requests that fail to find an available connection in the pool; thus, to reach 10 connections in the pool, the server would have to at some point have been processing 10 simultaneous requests in different threads. Optionally, you can supply the value THREADS in place of a numeric value, and the pool will be limited only by the number of threads in the process (up to 255). | |
| OwaStart oracle_start | This optional parameter allows you to specify the name of the default PL/SQL procedure to be run when no script name follows the location of the OWA in a URL. The specified procedure is called with no arguments. If not specified, mod_owa calls the OwaDocProc (if available) with "/" as the requested file. | |
| OwaBefore oracle_before | This optional parameter allows you to specify the name of a PL/SQL procedure that should be run just before the procedure requested. The procedure is called with no arguments. | |
| OwaAfter oracle_after | This optional parameter allows you to specify the name of a PL/SQL procedure that should be run just after the procedure requested. The procedure is called with no arguments. | |
| OwaProc oracle_proc | This optional parameter allows you to specify the name of a PL/SQL procedure to be substituted for all requests to the Location. The procedure will always be called with the flexible argument mode. If this parameter is set, it takes precedence over OwaStart. The document download parameters, if set, take precedence over this parameter. | |
| OwaRealm oracle_realm | This optional parameter signals mod_owa to use a new database session on every request, and to log the user in to the database using the username and password specified in the Basic authentication header. If no Basic authentication header is present, the user will be challenged using the realm specified by this directive. Note that use of this directive partially disables connection pooling/reuse for the location in question; connections are reused but a new session must be created on the connection for every request (adding significant overhead). | |
| OwaAlternate oracle_alt | This optional parameter allows you to specify the name of a PL/SQL package that provides an alternate implementation of OWA-like functionality. You may also specify flags that support enhanced functionality not available in OWA. The operation of this parameter is described in a later section. | |
| OwaUnicode oracle_uni | This optional parameter allows you to enable special Unicode binding modes for PL/SQL procedures call by mod_owa. If set to "USER", all calls to user procedures, including document action procedures, will be made using NCHAR/NVARCHAR bindings instead of the normal CHAR/VARCHAR binding. If set to "RAW", all calls to user procedures will be made using RAW bindings. If set to "FULL", all procedure calls including those for the OWA itself will be bound as Unicode. The operation of this parameter is more fully described in a later section. | |
| OwaReset oracle_rset | This optional parameter allows you to change the way mod_owa performs the RESET_PACKAGE operation. The default setting is "NORMAL", which uses the DBMS_SESSION RESET_PACKAGE interface, compatible with older databases. Newer installations (8.1.7.2 and higher) are encouraged to use "FULL", which calls the new MODIFY_PACKAGE_STATE interface but is functionally equivalent, or "LAZY", which also uses MODIFY_PACKAGE_STATE but merely marks packages for as-eneeded reinitialization. Users who have the most recent version of the OWA code and whose PL/SQL code is stateless/re-entrant can consider using "INIT", which simply calls HTP.INIT to reset the OWA itself, but leaves user package state undisturbed. | |
| OwaAdmin oracle_admin | This optional parameter allows you to restrict the use of the control/admin functions to a particular range of IP addresses. It is specified as an IP address prefix plus a mask. By default, mod_owa uses 255.255.255.255 for the mask, effectively disallowing control operations from all clients. | |
| OwaRound oracle_round | This optional parameter allows you to control the rounding of bind variables used as arguments to your procedures. Its use is described in a later section of this document. | |
| OwaSqlError oracle_error | This optional parameter allows you to specify a URL to be
called when mod_owa encounters SQL errors executing the action procedure
for a request. mod_owa will issue a redirect response to the browser to
the URL indicated, adding two arguments: | |
| OwaSession oracle_ses | This optional parameter allows you to specify a cookie name that mod_owa will take to carry a session identifier. mod_owa will associate the value of this cookie, if available, with the database connection that it uses to service the request. When attempting to find a connection from the pool, mod_owa will first try to reuse a connection bearing the same session identifier. The session identifier must match exactly (including case), and must be valid as a cookie value per HTTP (meaning it should be plain ASCII and not use certain punctuation values). Note that this parameter doesn't have any beneficial effect if Apache is running in a mode where worker processes are single-threaded. This parameter can be of use if your PL/SQL attempts to cache data for a user session to improve performance. It can also be used in conjunction with OwaRealm to increase the chances that a database session-create operation can be avoided for subsequent requests from the same user; the Basic authentication is still checked, just in case. The model is still assumed to be stateless, so session identifiers need not be unique or secure, and sessions need not be explicitly ended. (Most applications will, for other reasons, want to use unique, securely random session identifiers anyway, and to have some sort of session cleanup process independent of Apache.) There is no guarantee that subsequent requests will be serviced by the same session, even in the case of a multi-threaded Apache; any connection returned to the pool may still be used to service any request, as necessary. Use of this directive will cause mod_owa to prefer a fresh or unsessioned connection to reuse of a connection already associated with a different session. | |
| OwaDocProc document_proc | This is the name of a procedure to be called whenever a document request is to be handled. Its operation is described below. | |
| OwaDocPath document_path | This is a prefix that signals that the document procedure should be invoked on the specified URI to initiate a mime-typed download operation. Its behavior is also described below. | |
| OwaDocLong document_long | If set, this parameter signals that the document reader for paths with this prefix should use LONG or LONG RAW operation, as described below. | |
| OwaDocFile document_file | If set, this parameter signals that the document reader for paths with the OwaDocLong prefix should use the file system for storage, as described below. | |
| OwaDocGen document_gen | If set, this parameter signals that the document reader for paths with this prefix will generate dynamic content to be returned by GET_PAGE, as described below. | |
| OwaDocLobs document_lobs | If set, this parameter defines which LOB types to bind for document upload and download operations. The arguments are built in a pre-determined order, so specifying LOB types that occur later in the sequence automatically produces the binding for all earlier types. Its behavior is described in more detail below. If not set, the default is "NCHAR", which binds BLOB, CLOB, and NCLOB (compatible with earlier versions of mod_owa). Note that BFILEs cannot be bound for write, so specifying BFILE causes that binding to be used for reads only. | |
| OwaDocTable document_table | If set, this parameter causes mod_owa to use the WebDB interface for document upload and download operations. This directive should specify a table name for the document table, and a column name for the BLOB content type. Only BLOB operations are supported. Currently only downloads via BLOB are supported - the table/column are not actually used. | |
| OwaUploadMax upload_max | If set, this parameter limits the total size of a content upload to be no greater than the value specified. A "K" or "M" can be appended to a value to signal kilobytes or megabytes. | |
| OwaCharset dad_charset | This optional parameter specifies the character set to use for client interactions, because it's not possible to reliably determine this from browser HTTP requests. Data to/from the database will be transferred using this character set, overriding the NLS_LANG character set for this Apache location. Note that the DAD character set is specified using IANA's nomenclature, not Oracle's. | |
| OwaBindset dad_bindset | This optional parameter specifies the Oracle character set to use for binding arguments from requests that contain non-UTF8 byte sequences. Its operation is more fully described in a later section. Note that this character set is specified using Oracle's nomenclature, not IANA's. | |
| OwaDateFmt dad_datefmt | This optional parameter specifies the default date format to use for conversion of strings to/from the Oracle DATE data type. Normally, the environment variable NLS_DATE_FORMAT should be used in conjunction with NLS_LANG to control this value, however this provides a crude means of overriding the territory-specific value on a per-Location basis. The date format may contain spaces if enclosed in quotes, however mod_owa will truncate the format mask at the first such space found (the assumption is that this is the beginning of a time field). Against an Oracle 9i server, the string up to any second space character is used to set the NLS_TIMESTAMP_FORMAT and the full string is used to set the NLS_TIMESTAMP_TZ_FORMAT. Again, the preferred means for setting all these values is via environment variables. However, since the 9i values will not be understood by 8.x clients, this parameter provides an alternate way to set them and is the primary expected usage. |
|
| OwaTZ dad_tz | This optional parameter specifies a time zone for the Location. It works only with 9i servers; against older servers, it silently fails (and slightly increases the time required to create a new connection). The value would normally be set using the environment variable ORA_SDTZ, but that variable would then apply to all Locations, and moreover is only understood by 9i clients. This parameter allows you to specify different time zones for different Locations under the same Apache instance, and also allows you to set the time zone for 8.x clients (against a 9i server). |
|
| OwaCache oracle_cache | This is an optional parameter that can appear multiple times for any Location. It specifies a mapping for document storage between the logical (URL) names used by your PL/SQL code and a physical (file system) directory. The operation of this flag is described in the section on file system caching. | |
| OwaFlex oracle_flex | This is an optional parameter that can appear multiple times for any Location. It specifies the name of a procedure (or package.procedure) that should be handled with the flexible argument convention, even if the leading "!" is not present in the URI. Optionally, the package/procedure name can have a prepended "~" to indicate that the procedure should be called in the 2-argument mode. This feature is included primarily for compatibility with OAS, which apparently is able to do this automatically by describing procedures after the standard call convention fails. If you use this directive, put the most frequently-used procedures first because mod_owa does a linear search against them. | |
| OwaReject oracle_reject | This is an optional parameter that can appear multiple times for any Location. It specifies a string prefix for packages/procedures to be disallowed by mod_owa. For example, "OwaReject DBMS" disallows direct calls to all the DBMS_xxxx packages through the mod_owa interface. | |
| OwaEnv oracle_env | This is an optional parameter that can appear multiple times for any Location. It specifies an environment variable name and value to be passed to PL/SQL in the CGI context. For example, "OwaEnv MYVAR myval" adds an environment variable "MYVAR" with value "myval" to the CGI environment passed to PL/SQL by mod_owa. Note that OwaEnv has no effect on the environment used by the Apache worker processes. | |
| OwaLDAP oracle_ldap | This optional directive specifies a procedure for converting a basic authentication username/password and/or session cookie to a database username/password suitable for connection. It works in conjunction with OwaRealm and/or OwaSession and is described below. Note that use of this directive will add significant overhead to request handling, since a special database connection will be created to make the call to your procedure. | |
| Directive | Description | |
| OwaSharedMemory owa_shared_memory |
Controls the size and usage thresholds for mod_owa's shared memory segment. By default mod_owa doesn't create one. The exact syntax and effect of this parameter are described later in the section on shared memory caching. | |
| OwaSharedThread owa_shared_thread |
Controls the connection pool cleanup thread. By default, mod_owa doesn't create one. If set to a non-zero value, this parameter causes mod_owa to create a thread with the specified poll interval. | |
Apache's LocationMatch directive allows you to set up a Location where a variety of incoming URLs will be routed based on regular expression matching. Unfortunately, after processing the match, Apache will split the incoming URL at the first "/" found, regardless of the template used, such that the SCRIPT_NAME and PATH_INFO variables will not be as expected by the PL/SQL gateway. (In fact, this is true even with regular Location directives that contain "/"s.) For example, here is how Apache processes this Location and URL:
Location: /owa/subdir URI: /owa/subdir/mypackage.myprocedure Apache returns: SCRIPT_NAME = /owa PATH_INFO = /subdir/mypackage.myprocedure
Now consider this LocationMatch example:
LocationMatch: /owa/(cat|dog|horse) URI: /owa/horse/mypackage.myprocedure Apache returns: SCRIPT_NAME = /owa PATH_INFO = /horse/mypackage.myprocedure
In both cases, Apache has split the URI at the first "/" it encounters, despite what would actually be matched by the Location or LocationMatch directive.
mod_owa contains logic to detect and correct this condition before the CGI environment is passed to PL/SQL. This logic works well for the Location case. In the LocationMatch case, mod_owa uses Apache's regular expression engine to reparse the URI after first reassembling it from SCRIPT_NAME and PATH_INFO.
When matching against the regular expression, mod_owa must still break the SCRIPT_NAME and PATH_INFO at a "/". Any LocationMatch directive that does not have a trailing "/" will have one appended by mod_owa for this purpose. The regular expression processor will attempt to apply each rule as many times as possible, and this can lead to consumption of too much of the URI string for poorly-formulated expressions. For example, consider the effect of a wildcard match on a document-like URI:
LocationMatch: /owa/.* URI: /owa/horse/docs/images/photo.gif mod_owa produces: SCRIPT_NAME = /owa/horse/docs/images PATH_INFO = /photo.gif
(the implied trailing "/" added by mod_owa forces the wildcard to stop at "/photo.gif"). This was probably not the desired split, and instead, this formulation should have been used:
LocationMatch: /owa/[^/]* URI: /owa/horse/docs/images/photo.gif mod_owa produces: SCRIPT_NAME = /owa/horse PATH_INFO = /docs/images/photo.gif
This code is still very experimental and subject to change. In particular the assumption of a trailing "/" is still open to question.
Request setup
The arguments arrive via either the GET or POST methods in a single tokenized string. GET passes them in the URL, while POST makes them available in the content body (and therefore can accomodate much larger data sets). The arguments are parsed and unescaped by searching through the argument string, assumed to be in the following format:
<param1_name>=<param1_value>&<param2_name>=<param2_value>...
The un-escaping is done after each name/value pair is parsed off.
Note that for POST requests, any arguments found in the URL are merged with the arguments from the content body. URL arguments are processed first.
Request execution
0. Create database connection
This is generally done only if a previous connection is not available in the pool for reuse. (More on the connection pool below.) On creation, the connection is set to the NLS parameters (if specified) with the following commands:
alter session set NLS_LANGUAGE='AMERICAN'
^ Specified language goes here
alter session set NLS_TERRITORY='AMERICA'
^ Specified territory goes here
alter session set TIME_ZONE='US/Eastern'
^ Specified time zone goes here
alter session set NLS_DATE_FORMAT='DD/MM/YYYY'
^ Specified format mask goes here
alter session set NLS_TIMESTAMP_FORMAT='YYYY/MM/DD HH24:MI:SS'
^ Specified format mask goes here
alter session set NLS_TIMESTAMP_TZ_FORMAT='YYYY/MM/DD HH24:MI TZH:TZM'
^ Specified format mask goes here
1. Initialize PL/SQL state
This operation is required because the OWA leaves state from previous request executions lying around after a return. A better design would make it unnecessary, and probably be much more efficient. For now, all PL/SQL package state must be zeroed on every request, and this is done with the following PL/SQL anonymous block:
begin DBMS_SESSION.RESET_PACKAGE; end;
Note that this command is always the same on every request. mod_owa will keep a statement handle open for this operation and reuse it whenever the connection is reused. (This is sometimes called a database "cursor".) It's the first of five statement handles that are held on every connection in the connection pool.
In the latest version of mod_owa, I've moved this operation to the end, so it now occurs after the final GET_PAGE or other content transfer occurs. In effect, I've gone from a model where the connection is cleaned up prior to every request to a model where the connection is cleaned up after every request. I did this to improve perceived performance, because the cost of the RESET_PACKAGE operation will now be incurred after the user has received his/her content, not while he/she is waiting for it. The difference is largely theoretical: so far I haven't noticed any actual improvement (but then, I don't have a high-volume site where RESET_PACKAGE becomes costly, either).
Via the OwaAlternate directive, you can switch off this operation via a directive such as:
OwaAlternate MY_OWA KEEPSTATE
This can greatly decrease the load on your database for high-volume sites. To use this mode, you must ensure that all of your application's PL/SQL code is re-entrant, meaning that it leaves no information in PL/SQL package globals that would prevent the servicing of a subsequent HTTP request for a different client. As an example, code that performed a one-time initialization of globals for the particular user involved in a request would not be re-entrant, whereas similar code that kept such information in a global cache that was resettable (or that could hold the information for multiple users) could be re-entrant. Unfortunately the code of the OWA itself is not yet re-entrant, so this requires that you supply your own OWA-equivalent implementation.
Short of disabling the reset operation entirely, you can use the OwaReset flag to alter the nature of the reset call performed, as follows:
| NORMAL | The default, calls DBMS_SESSION.RESET_PACKAGE. | |
| FULL | Calls DBMS_SESSION.MODIFY_PACKAGE_STATE(1). This mode is recommended for more recent database versions because RESET_PACKAGE is being deprecated. It is functionally equivalent to RESET_PACKAGE. | |
| LAZY | Calls DBMS_SESSION.MODIFY_PACKAGE_STATE(2). This avoids flushing the cursor cache for PL/SQL static SQL statements, and defers the work of actual package reinitialization until a package is actually called. This mode is recommended if you have a newer version of the database. | |
| INIT | This mode assumes that your PL/SQL code is re-entrant, and doesn't need to be reset. The full reset is avoided completely; instead, HTP.INIT is called to reset the state of the OWA itself. |
2. Set up the CGI environment
mod_owa must pass the name/value pairs from the CGI environment into the OWA before it can execute your procedure, so that their values will be available to your code. This is done by executing the following PL/SQL anonymous block:
begin OWA.INIT_CGI_ENV(:ecount, :namarr, :valarr); end;
The arguments are as follows:
ecount Number of name/value pairs for the CGI environment namarr PL/SQL table of records with a list of VARCHAR2 names valarr PL/SQL table of records with a list of VARCHAR2 values
Note that the last two arguments to the procedure are actually arrays, while the first is a scalar binding.
This statement is always the same on every call, though the values in the binding buffers are obviously different. mod_owa keeps this statement handle open so that it can reuse it whenever the connection is reused. This is the second of five statement handles held on pooled connections.
3. Pass basic authentication information
mod_owa will pass the username and password from any Basic authentication header received by Apache using the following PL/SQL anonymous block:
begin OWA.USER_ID := :usr; OWA.PASSWORD := :pwd; end;
Like the previous statements, this statement is always the same on every call, but with different values bound for username and password. mod_owa keeps this statement handle open so that it can reuse it whenever the connection is reused. This is the third of five statement handles held on pooled connections.
These assignments could (and should) be executed as part of the call to INIT_CGI_ENV, but a bug in PL/SQL has forced me to do this in a separate operation.
4. Execute the requested procedure
This is the only truly dynamic SQL in the entire process. The code that must be executed is normally of the following form:
begin
if <authorization_call> then
<before_procedure_call>
<procedure_call>(<arg1_name> => :B1, <arg2_name> => :B2, ...);
<after_procedure_call>
commit;
else
:realm := OWA.PROTECTION_REALM;
end if;
end;
The authorization call has three forms. In older versions of OWA, it was OWA_INIT.AUTHORIZE(), while in newer versions it's OWA_CUSTOM.AUTHORIZE(). If PACKAGE is specified for the Location, the check is <package name>.AUTHORIZE(). mod_owa will build one of these based on what you tell it in the httpd.conf Location. It will also simply skip the check if that's what you specify.
The essential part of the call is of course the procedure and argument bindings. There are four supported modes of argument passing:
| named arguments | The arguments are bound by naming the binding explicitly, so that the call is position-independent. | |
| positional arguments | Similar to the first form, but the names are skipped and therefore argument order matters. | |
| flexible arguments | The arguments are passed as arrays of name/value pairs (e.g. argc/argv style). | |
| raw post data | The data is not interpreted in any way, but is passed as a single raw parameter. |
The procedure name is taken from the URL, it's the name appearing right after the "mount point". For example,
http://mymachine/owa/foobar?a=dog&b=cat
is a call to PL/SQL procedure foobar (with two arguments). This call requests the default passing mode, which is named arguments, and would produce something like the following:
foobar(a=>:B1,b=>:B2);
Bind variables are used for the arguments so that the internal cursor sharing mechanisms of the database can match this with other requests for the same function.
Special characters in the procedure name signal which type of argument passing to use. To get positional argument passing, precede the procedure name with a circumflex as in:
http://mymachine/owa/^foobar?a=dog&b=cat
which results in:
foobar(:B1,:B2);
To get flexible argument passing, precede it with an exclamation mark:
http://mymachine/owa/!foobar?a=dog&b=cat
which always results in the same call regardless of the number of arguments:
foobar(:B1,:B2,:B3,:B4); :B1 is the number of name/value pairs :B2 is an array of parameter names, in this case the array ['a','b'] :B3 is an array of values, in this case ['dog','cat'] :B4 is an array reserved for future use
Note that OAS supported another flexible argument mode, which mod_owa will try if the 4-argument mode fails (and if you have not used the STRICT setting for OwaDescribe):
foobar(:B2,:B3); :B2 is an array of parameter names, in this case the array ['a','b'] :B3 is an array of values, in this case ['dog','cat']
To get the raw argument data without any escaping or interpretation by Apache and/or mod_owa, precede the call with a tilde, as in:
http://mymachine/owa/~foobar
This mode is not part of the official OWA specification, and is described in a later section.
Binding of arguments is done as a series of string values sized based on the actual data. In a request it is possible to pass multiple elements with the same name (typically this happens with POST requests where form elements are repeated on multiple lines). For example:
http://mymachine/owa/foobar?n=2&a=dog&a=cat
seems to provide two values for "a". mod_owa treats this as an array-bound parameter to the PL/SQL routine, and will pass it in as a table of records of VARCHAR type. Thus, the above URL results in:
foobar(n=>:B1,a=>:B2);
where :B1 is a scalar string "2" and :B2 is the array ['dog','cat'].
Special notes on bind modes:
<form action="/owa/myprocedure" method="GET"> <input type="image" name="mybutton" src="mybutton.gif"> </form>
myprocedure?mybutton.x=123&mybutton.y=321
myprocedure(mybutton=>:B1);
The final clause of the statement retrieves a protection realm which can optionally be used to drive HTTP Basic authentication. This value is retrieved only if the AUTHORIZE function fails, and it is not bound if authorization is disabled. You can optionally set this value directly (or via OWA_SEC.SET_PROTECTION_REALM), and this will cause mod_owa to skip the GET_PAGE operation (described below) and send back a Basic authentication challenge for the realm provided. (This mode of operation is provided for compatibility with OWS.) If you do not set this value, mod_owa proceeds with the GET_PAGE operation and assumes that you've generated an appropriate error or login page during the AUTHORIZE call.
The procedure call should result in the creation of HTTP content for transmission back to the client. This call is therefore usually the most time-consuming part of handling a request. Another statement handle is needed for this operation, though because the SQL changes from request to request, mod_owa must reparse the statement each time (however, by using bind variables, mod_owa expects to hit the data server's shared cursor cache most of the time anyway). This is the fourth of the five statement handles held for pooled connections.
5. Get and return data
After the execution, mod_owa is ready to retrieve the HTML output data, which is held in PL/SQL global memory in a table of records. This is done by repeated calls to the following PL/SQL anonymous block:
begin OWA.GET_PAGE(:linearr, :nlines); end;
The first argument is an OUT-only table of records, holding up to 256 lines of output, while the second argument is an IN/OUT count of the number of lines available/returned by the call.
This is the fifth statement handle held on every connection in the pool, and, like the first three, it never changes, so it's kept parsed and ready for re-execution on subsequent requests. It's called repeatedly to get the output until the number of lines returned is less than 256, signalling the end of the HTTP result.
The output returned is streamed to the Apache request response. However, it is necessary to parse it for an HTTP header, since OWA unfortunately doesn't separate the header return from the content (as demanded by Apache itself). Header elements are of the form:
<tag>: <value><newline>
Of course, some lines of content may also have this form. The header is separated from the content by a blank line, so mod_owa looks for this blank line when examining the return from GET_PAGE. Unfortunately the OWA doesn't always return a header, and since content can also have blank lines, there is no absolutely reliable way to separate the two. mod_owa uses a heuristic technique that seems to work well. In cases where OWA does return a header, it always returns a CONTENT-TYPE: tag. mod_owa looks for this tag anywhere in the first 256 lines it fetches, and, if found, assumes there's a header and proceeds to process all lines as header lines until it sees the blank-line separator. If, however, it doesn't find this tag, then mod_owa assumes there's no header and it's all content.
Header elements include the CONTENT-TYPE tag, cookies, etc. They are all intercepted and merged with the request response header. After the header is exhausted, the remaining content is simply streamed to the request output.
Under certain special conditions, the OWA can return a header with no content. Moreover, these headers don't have a CONTENT-TYPE marker (because, of course, they have no content!). The three that I am aware of are the redirection header (signalled by the lone LOCATION: tag), the refresh header (signalled by the lone REFRESH: tag), and the authentication challenge (signalled by the WWW-AUTHENTICATE: tag). mod_owa has special-cased code to handle all these cases.
Binary content types should, in general, not be returned via this interface. It is possible in most cases to convey such content back via the file download interface (discussed below). The main problem with binary content types is simply that the OWA.GET_PAGE interface, as well as all PL/SQL-side processing done by the OWA, is character-based, exposing attempts to transmit binary data to the following problems:
Despite these problems, some users still return binary data through the OWA interface, using the CHR() function to set specific byte values. This will generally work under the following conditions:
The mod_owa version of this functionality is still experimental (more so than the rest of the code). There are still numerous open issues, particularly relating to uploads and character-set issues for CLOBs. The HTTP standard for file uploads is particularly toxic, and my understanding of it is constantly being proven incorrect; as a result, the quality of mod_owa's upload parsing code is low. These issues are noted in the descriptions that follow.
It was not possible to exactly mirror the specification of this feature in Oracle's version of the gateway, mainly because that specification required the installation and use of WebDB-related PL/SQL facilities that aren't supportable by mod_owa. The mod_owa version of this functionality attempts to provide a relatively simple, entirely programmatic interface, by means of which programmers can "wrap" their own tables, access control functions, etc.
File download
This is by far the simpler and more robust portion of the implementation, particularly if you use only the BLOB (binary) files. The functionality is governed by six parameters:
I will describe the operation of OwaDocLong, OwaDocFile, and OwaDocGen in the next sections, so what follows here pertains to the LOB-based interface.
When a URI is received prefixed by the OwaDocPath, the entire URI (minus any query string) is considered to be a logical file path, and this file path is passed to the OwaDocProc, along with any other arguments specified by the query string. For example, consider the following URI:
http://mymachine/owa/docs/a/b/c/doc.gif?arg1=1&arg2=2
^ OwaDocPath
To keep the interface standard despite the possibility that the arguments may not be pre-determined, the call is always made using the flexible argument mode (described previously). In addition, 2-5 extra arguments are added, so the call becomes similar to the following:
begin <OwaDocProc>(:B1,:B2,:B3,:B4,:B5,:B6,:B7,:B8,:B9); commit; end; :B1 is an input with the number of name/value pairs :B2 is an input array of parameter names :B3 is an input array of parameter values :B4 is an input array reserved for future use :B5 is an output string for the document mime type :B6 is an output BLOB locator for binary documents :B7 is an output CLOB locator for textual documents :B8 is an output NCLOB locator for Unicode textual documents :B9 is an output BFILE locator for binary file-based content
The first element in the array of values will contain the logical file path (it's also available in the CGI environment). Any other arguments found in the query string will follow this one in the arrays.
For example, with the URI shown previously, the following arguments would be passed as inputs to the OwaDocProc:
:B1 => 3
:B2 => {"document_name", "arg1", "arg2"}
:B3 => {"/docs/a/b/c/doc.gif", "1", "2" }
:B4 => {"", "", "" }
The call is made without the usual authorization check being built into the anonymous block. The presumption is that any necessary access control is done by this procedure. If authorization fails and/or the document cannot be found, or if any other failure occurs, return NULLs for all LOB locators to signal an error to mod_owa. The module will then assume that the OwaDocProc has written a response page in the normal manner and will use OWA.GET_PAGE in the normal manner to retrieve and display it.
If the call succeeds, then return a mime type string (if possible) and return a LOB locator selected from the underlying storage table. The mime type string can be up to about 4000 bytes long. The procedure should return a non-null value in one of the LOB handles, and nulls in the others (if multiple non-null LOBs are returned, mod_owa will use the first one it finds in sequence starting from the BLOB, and ignore the others).
The setting of OwaDocLobs governs which of these arguments mod_owa will bind. The settings are as follows:
| BIN | Binds only the BLOB argument (6 arguments total). | |
| CHAR | Binds the BLOB and CLOB arguments (7 arguments total). | |
| NCHAR | Binds the BLOB, CLOB, and NCLOB arguments (8 arguments total; this mode is the default and is compatible with APIs coded for older versions of mod_owa). | |
| FILE | Binds all four LOB types, including BFILE. |
If you do not return a mime type, mod_owa will assume a type of "application/octet-stream" for binary LOBs and "text/plain" for character LOBs.
Also, mod_owa will attempt to determine the mime type based on the file extension, if it can find one. Right now this is a hack because I can't find any native Apache API for this, so there's a hard-coded table in mod_owa for some of the more common extensions. If you want to be sure to avoid this, return a mime type from the OwaDocProc.
BLOBs (and BFILEs) will be streamed down in a straightforward fashion. For CLOBs and NCLOBs, mod_owa will convert the content to the DAD character set in a manner similar to the way it handles OWA.GET_PAGE requests. Because LOB semantics work in characters, it is not always possible to determine the "Content-Length" for a character-based download. mod_owa will use the character length of the LOB as the content length only if you are running a single-byte character set (such as "iso-8859-1").
The semantics of the mime type argument have been extended to allow your code to return additional header elements for the document, up to 4000 bytes total. To return such elements, separate them from the mime type (and each other) with newlines. If there is no mime type being returned, you must still begin the buffer with a newline. The last header element need not be terminated with a newline. As an example:
text/html; charset=iso8859-1\n Last-Modified: xxx\n If-Modified-Since: xxx
File download with LONG or LONG RAW
This optional mode is signalled by any document path whose prefix matches the value set for OwaDocLong. The operation is in most respects similar to that for the LOB read, so in this section I will describe only the differences. The main difference is that the read procedure call is built as follows:
begin <OwaDocProc>(:B1,:B2,:B3,:B4,:B5,:B6,:B7,:B8); commit; end; :B1 is an input with the number of name/value pairs :B2 is an input array of parameter names :B3 is an input array of parameter values :B4 is an input array reserved for future use :B5 is an output string for the document mime type :B6 is an output string for returning a SQL select statement :B7 is an output string for returning a SQL bind variable string value :B8 is an output string for returning the 1-character value of a RAW flag
Note that the same OwaDocProc name will be used for this mode; the implementation can either overload the function (recommended) or make this the only mode of operation by setting OwaDocLong = OwaDocPath in the configuration.
The function is expected to return a SQL statement suitable for use by mod_owa to fetch back the LONG or LONG RAW value. The SQL statement should be a SELECT statement with a single column return value of type LONG or LONG RAW. The statement may optionally contain a single bind variable, typically the value for the primary key of the underlying table, or a ROWID. If the SQL statement requires this bind, return a value for it in the appropriate argument, otherwise return a NULL value. The SQL statement can be up to about 32512 bytes in length (the PL/SQL maximum). The bind value can be up to about 2000 characters long. The last argument is a flag to indicate whether the returned column is a character-based LONG or a binary LONG RAW type. Return a NULL for the character-based type, otherwise return any single-character value (typically, a 'Y'). The mime type argument is used in a manner similar to that for the LOB case; as with LOBs, the mime type can be up to about 4000 bytes long.
As an example, the SQL statement you return should be of the following form:
select LONG_CONTENT from MYDOCUMENTS where KEY_COLUMN = :B1;
The module will run the statement you specify, with the bind variable (if any), and perform a piecewise fetch of the result, streaming this back to the requestor. To signal an access control failure or other error, return a null for the SQL statement; as with LOB-based downloads, mod_owa will then assume that your code has written an OWA response page in the normal fashion, and attempt to display it using OWA.GET_PAGE. The returned contents can be up to 2 gigabytes in length.
As with LOBs, LONG RAWs work without any character set issues because they are transferred entirely in binary. (If a return character set is needed, it can be attached to the mime type value you return, as described earlier.) For LONGs, the transfer mode causes the character data to be converted to the character set of the Apache server prior to transfer, so no special handling is performed.
There may, however, be issues with the way the piecewise functions count lengths; it's not clear whether they always count in bytes, which the current code assumes, or whether some return lengths are in characters, in which case these transfers will have the same issues as CLOBs. Because of the way the code is written, any single-byte character set should work OK, without the data stripping done for CLOBs.
It might be more flexible to implement this using the REF CURSOR mechanism. With this mechanism, PL/SQL can return a statement in an already-executed state, such that mod_owa need only run the piecewise fetch to stream the result. Unfortunately, this technique wouldn't work on older versions of the database (the main target of this legacy-mode feature). Also, REF CURSORs can't be used for INSERT/UPDATE operations, so mod_owa's support for uploads to LONG or LONG RAW targets would be forced to have an interface similar to this one anyway.
Unlike LOBs, Oracle's LONG and LONG RAW types don't provide any means for determining the length of the content, so the above linkage unfortunately doesn't support the transmission of a Content-Length for the request. For these legacy types, mod_owa has two special modes of operation, governed by additional parameters on the OwaDocLobs directive:
<OwaDocProc>(:B1,:B2,:B3,:B4,:B5,:B6,:B7,:B8,:B9);
select LONG_CONTENT, CONTENT_LENGTH from MYDOCUMENTS where KEY_COLUMN = :B1;
You can specify this setting in combination with the LOB-related settings for OwaDocLobs, as in:
OwaDocLobs CHAR LONG_FETCH_LENGTH
File download using the file system
This mode is an override of the legacy LONG and LONG RAW mode. It is signalled by setting OwaDocFile. The operation is in most respects similar to that for the LONG reads, so in this section I will describe only the differences. The main difference is that the read procedure call is built as follows:
begin <OwaDocProc>(:B1,:B2,:B3,:B4,:B5,:B6); commit; end; :B1 is an input with the number of name/value pairs :B2 is an input array of parameter names :B3 is an input array of parameter values :B4 is an input array reserved for future use :B5 is an output string for the document mime type :B6 is an output string for returning a file system path
Note that the same OwaDocProc name will be used for this mode; the implementation can either overload the function (recommended) or make this the only mode of operation by setting OwaDocLong = OwaDocPath in the configuration. Also note that you must set OwaDocLong, since that parameter is still needed to distinguish this read mode from the standard LOB-based mode.
The function is expected to return a file path name that mod_owa can use to retreive the contents of the document. mod_owa will prepend the directory from OwaDocFile - mod_owa expects that the path name returned by the procedure begins with a path separator (e.g. "/") and that the path in OwaDocFile does not end with such a separator.
Unlike the LONG/LONG RAW mode, there is no distinction between binary and text data with file system operation.
Dynamically-generated virtual files
If the path prefix matches the value of the optional parameter OwaDocGen, then this signals mod_owa to call your OwaDocProc with just the normal flexible arguments, and no output arguments, as shown here:
begin <OwaDocProc>(:B1,:B2,:B3,:B4); commit; end;
You then write the content using the normal OWA facilities, and mod_owa will retrieve it with GET_PAGE as per any normal request. The main difference versus a standard request is that the URL transmitted by the browser appears to be a normal file path (unless you allow arguments).
It is possible to use temporary LOBs to achieve a similar effect using the LOB-based transfer mode described earlier. However, there is no similar concept for LONG or LONG RAW types, so this method is the only way to do dynamic virtual files on an older (pre-8i) data server,
Another possible use for this directive is for streaming generation of the content via an alternate OWA interface. With Oracle's OWA, you must complete all of the output operations before returning to mod_owa, because once the GET_PAGE loop begins, you will not have any further opportunity to generate content. However, with an alternate OWA, you could simply save the page-generation context during the call to OwaDocProc, and then generate portions of content on demand during the GET_PAGE phase.
File upload
A file upload operation is triggered whenever the CONTENT_TYPE for a request is "multipart/form-data". The module assumes that the incoming stream contains a series of arguments and file streams delimited by the "boundary" string generated. This is typically accomplished by adding the enctype attribute to the form tag in the page generating the upload operation, as shown here:
<form action="doc_pkg.writefile"
method="POST" enctype="multipart/form-data">
...
</form>
Note that upload processing takes precedence over any alternative handling that might be implied by other mod_owa directives. For example, if the incoming URL matches the structure for a document download, the document download procedure will not be called.
Unfortunately, HTTP mixes both the form arguments and the file contents into a single stream. It provides no lengths for individual portions of the stream, and mixes binary information with textual information. In short, it's a mess, difficult to parse reliably. An example of an incoming stream might be as follows:
-------------------------1234567890\r\n Content-Disposition: form-data; name="field1_name"\r\n \r\n field_value\r\n -------------------------1234567890\r\n Content-Disposition: form-data; name="field2_name"; filename="foo.doc"\r\n Content-Type: mime/type\r\n ...other optional header elements such as Content-Encoding...\r\n \r\n ...file contents...\r\n -------------------------1234567890--
The incoming stream will be parsed in the order elements are received. The stream may contain both normal arguments and files. To avoid the need to buffer the entire stream before calling the action procedure, mod_owa will call the action procedure whenever a file is encountered in the stream, passing whatever arguments it has accumulated to that point. Users would therefore be well-advised to ensure that all non-file arguments in the form appear before the file input types.
The action procedure will be called once per file found in the stream, plus one additional time to generate a response page to the request. The action procedure will be called even if the file input element is blank. The module builds the call to the action procedure using the flexible argument model, with additional arguments for LOB handles, as shown here:
begin <action_procedure>(:B1,:B2,:B3,:B4,:B5,:B6,:B7,:B8); end; :B1 is an input with the number of name/value pairs :B2 is an input array of parameter names :B3 is an input array of parameter values :B4 is an input array reserved for future use :B5 is an input/output string for the document mime type :B6 is an output BLOB locator for binary documents :B7 is an output CLOB locator for textual documents :B8 is an output NCLOB locator for Unicode textual documents
The action procedure should return a valid LOB handle in one of the LOB output arguments. If nulls are returned for all of them, mod_owa assumes that an authorization problem or other error has occurred, and will skip that file in the incoming stream. Otherwise, mod_owa will stream the file to the first non-null LOB handle in sequence and commit the transaction.
The LOB arguments bound are also subject to the value of OwaDocLobs, as described in the section on file downloads. The only difference is that a BFILE locator is never bound, so the FILE setting causes only 8 arguments to be built for writes (this is because Oracle doesn't support writes to BFILEs).
Note the following differences versus the OwaDocProc described for reading files, and versus other calling modes:
The name/value argument arrays will contain every argument found in the content stream to that point. The first element in the values array will always be the value of "OwaDocPath" (to support programmers that want to use a single implementation of the code against multiple mod_owa locations), and the second element in the array will always be the name of the fi