Just like AOLserver provides a series of ns_... APIs
for getting and setting HTTP connection-level information in Tcl code,
and writing output to the browser, JSPs and the Servlet API provide
similar functionality for Java web applications through the interfaces
in the javax.servlet package hierarchy. Sun provides a
reference for servlet and JSP interfaces as part of the Java 2
Enterprise Edition (J2EE) API documentation at http://java.sun.com/j2ee/j2sdkee/techdocs/api/index.html.
There are some major differences between JSPs and Tcl/ADP pages, though:
db_1row and ad_page_contract,
directly as Java methods. Instead, we implement these APIs as macros
to be handled by a precompiler; the precompiler generates legal
JSP code with the proper object declarations.
String x = "34" and then assign
int y = x directly. The Java library contains
several routines for converting string literals to numeric values and
vice versa.
All JSPs (whether part of ACS or not) declare several objects implicitly; the most important are:
| Object Name | Object Type |
|---|---|
request |
javax.servlet.http.HttpServletRequest |
response |
javax.servlet.http.HttpServletResponse |
| out | javax.servlet.jsp.JspWriter |
out object is used implicitly every time you output
HTML in a JSP. You can use JSPs in a
since literal HTML within a JSP is translated to
statements like this:
out.println("<h4>hello world!</h4>");
For most connection-level operation you can do in AOLserver, there is a corresponding method in Java. Some examples:
| Method | Tcl/AOLserver | Java/JSP |
|---|---|---|
| Sending a redirect. | ns_returnredirect $return_url
| response.sendRedirect(return_url);
|
| Returning content. | ReturnHeaders 200 image/jpeg
| response.setStatus(200);
|
| Determining an IP Address | set ip [ns_conn peeraddr]
| String ip = request.getRemoteAddr();
|
The request object also has an attribute list associated with it,
which can be used for passing objects from one servlet or JSP page to
another. This is done by calling request.setAttribute("name",
object) and request.getAttribute("name"). The
request object also has methods for retrieving URL/form variables and
getting properties about the connection.
Getting started
Flow of a typical ACS Java page
Most ACS Java pages will follow a similar flow pattern, which is
nearly identical to most ACS Tcl pages:
Here is an example from the News application.
ad_page_contract
macro
db_* macros,
which can implicitly declare the datasources to pass to the template
ad_return_template macro, passing the datasources listed
in the -properties section of ad_page_contract
|
Setup standard
|
web.xml deployment
descriptor.
Each ACS Java page is implemented with two files: a JSP with an
.ajp (ACS Java Page) file extension, that does business
logic, database access, user input validation, etc.; and a
corresponding ADP template, with an .adp extension, in
the same template format used by ACS Tcl.
The .ajp extension on the logic page indicates that it
is to be handled by the macro precompiler first, which generates a
.generated.jsp JSP page and then executes the generated
page.
The .adp templates are also handled by a
precompiler that translates ATS templates into JSP code.
The skeleton of most ACS Java JSP pages will look like this:
<%@ include file="/acs.jspi" %>
<-- this imports several Java packages and ACS libraries for use in
the JSP page --%>
<%
// we have escaped into Java code
ad_page_contract {
this is a simple test page
} {
{input_param1 ""}
input_param2:integer
} -properties {
output1:multirow
output2:onevalue
}
// ... do queries, DML, etc. here ...
String output1 = "hello world!";
db_multirow output2 {
select a, b, c
from table
where a > :input_param2
and b = :input_param1
}
// now output1 and output2 are both ready to be passed to the
// template
ad_return_template
%>
Form and URL variables are processed with the
ad_page_contract macro. This macro uses nearly the same
syntax as the procedure with the same name in ACS Tcl; it performs
some type checking and validation, and displays an error if a required
parameter is missing or a parameter is the wrong type. The
translation of the ad_page_contract macro declares local objects
corresponding to each input form/URL variable for later use in the JSP
page.
The syntax for the ad_page_contract macro is:
ad_page_contract { ... comment ... } [{ var1[:flags] var2[:flags] ... }] [-validate { .. validation code ... }] [-errors { .. error code .. }] [-properties { .. datasources to export to template .. }]Available flags for each input variable include:
You can issue additional complaints back to the user using the
- integer -- the supplied variable must be an integer and an error will be displayed if otherwise; the object created will be a
java.math.BigDecimal.- naturalnum -- An integer >= 0.
- boolean -- the declared variable will be a boolean (true/false).
- optional -- the variable is optional; will be set to null if not present.
- notnull -- the variable must be present in the request, and non-empty.
- multiple -- there is more than one variable with the given name; the variable declared will be an array
type []of the base type- list -- there is more than one variable with the given name; the variable declared will be a
java.util.Listof the base type- array -- if the input variable name is "var", then all form variables var.key1, var.key2, etc. will be combined into a single
java.util.Mapobject named "var", with keys key1, key2, ...; var.get("key1") will return the value of form variable var.key1, and so forth.- date -- if the variable name is "var", then the form variables var_month, var_year, and var_day are combined to form a date. The resulting object is of type
java.sql.Date.- file -- the variable named will be a String with the name of the file that the client uploaded, on the client side. An additional variable named ${var}_tmpfile will be created with the path to the contents of the uploaded file on the server.
ad_complainmacro within the-validateblock. These complaints will all be displayed together to the user when thead_page_contractmacro is finished executing.Here is a detailed example from
acs-admin/apm/version-edit.ajp.%@ include file="/acs.jspi" %> <%@ page import="com.arsdigita.acs.acsAdmin.*" %> <%@ page import="com.arsdigita.apm.*" %> <% ad_page_contract { Edit a package version @author Bryan Quinn (bquinn@arsdigita.com) @date 17 April 2000 @cvs-id version-edit-2.tcl,v 1.4 2000/10/13 16:55:32 bquinn Exp } { version_id:naturalnum,notnull version_name version_uri summary description {description_format ""} { owner_name:multiple} { owner_uri:multiple} vendor vendor_uri {release_date ""} { upgrade_p "0" } } -validate { version_changed_ck -requires {version_id version_name version_uri} { // The user has to update the URL if he changes the name. db_1row old_version_name_query { select version_name old_version_name, version_uri old_version_uri from apm_package_versions where version_id = :version_id } boolean version_changed_p = version_name.equals(old_version_name); if (version_changed_p && version_uri.equals(old_version_uri)) { ad_complain } } } -errors { version_changed_ck {You have changed the version number but not the version URL. When creating a package for a new version, you must select a new URL for the version.} } ... %>
db_multirow,
db_foreach, db_1row, and
db_string, depending on whether the query is for multiple
rows, one row, or one value. Also, you may use the
db_sql macro to declare a SQL code fragment to include in
another SQL statement, for building dynamic queries.
Bind variables are handled by the macro precompiler. Whenever
":foo" occurs within a SQL statement, the macro precompiler will
bind the current value of the Java object foo into
the designated position in the SQL statement. Named binds should
always be used for in vars; ":[number]" and "?" are assumed to be
out vars.
The database query macros generally declare objects for use in
the JSP page. The db_1row and db_foreach
macros declare an object corresponding to each column name returned
by the query. They, along with db_multirow, also result
in the declaration of a java.sql.ResultSet. The db_string
macro just returns a string and doesn't declare any variables.
Example:
<%
ad_page_contract {
simple example page to show db_multirow and db_1row in action
} {
group_id:notnull
{stub ""}
} -properties {
topics:multirow
}
String s = [db_string getTwo "select 1 + 1 as two from dual"];
// s is "2"
SqlFragment sqlf = [db_sql clause "and topic_name like '%' || :stub || '%'"];
// interpolate this sqlf fragment later
db_multirow topics {
select * from bboard_topics
where group_id = :group_id
$sqlf
}
// topics is now declared as a java.sql.ResultSet
db_1row get_date {
select sysdate from dual
}
// now get_date is a ResultSet, with the cursor pointing at the first
// row; and sysdate is a java.lang.Object with the current date as a string.
// now display output: we're going to be bad in this example and
// display directly in the JSP rather than using the template.
%>
<%= get_date.getString("sysdate") %> is the same as <%= sysdate %>.
<ul>
<% while (topics.next()) { %>
<li><%= topics.getString("topic") %>
<% } %>
</ul>
| Java and Tcl |
|---|
|
There is an analogous db_exec_plsql method as well, for
calling stored functions and procedures. This is also nearly
equivalent to its Tcl counterpart:
| Java and Tcl |
|---|
|
db_call_plsql macro as a shorthand
for calling stored functions, eliminating the need for the
BEGIN...END block:
| Java only |
|---|
|
@ds.clob_column@
will display the contents of clob_column in a datasource
whether it is a CLOB, CHAR, or VARCHAR.
BLOB content may be retrieved with the static
getBLOB() method in
com.arsdigita.db.OracleUtils; the dumpBLOB
method in the same class may be used to write the BLOB content
directly to an output stream.
To perform DML operations with a CLOB or BLOB, you can use the
-blob_file switch to db_dml. There are three
important differences between this and the analogous operation in Tcl:
returning col into ?
begin ... end;" in order to make the
returning into ? clause work. You must also end your
INSERT or UPDATE statement with a semicolon.
Example:
| Java | Tcl equivalent |
|---|---|
|
|
db_transaction macro. Nested transactions are supported
Example:
| Java | Tcl equivalent |
|---|---|
| |
To pass control to an .adp template, use the
ad_return_template macro. In /path/to/foo.ajp, the
ad_return_template macro will pass control to the template
/path/to/foo.adp.
Note that AJP files are really just JSP files with a different
extension, and the AJP extension indicates that the file is to be
preprocessed and translated into a JSP by the
MacroTranslator class before it is executed. Also, .adp
templates are ultimately translated into JSP code as well, by the
TemplateTranslator class.
| Tcl/AOLserver | Java/JSP |
|---|---|
ns_log notice "Sending email..."
| |
ns_log warning "Warning: ..."
| |
ns_log error "Oracle down, major system failure ..."
| |
| ns_log debug | Log.debug("just assigned new user session");
|
The following steps take place within the RequestProcessor to serve a response to the end-user's browser:
/path/pageis:
Note that requests for abstract URLs that map to static content will go through the RequestProcessor twice: the first time, the abstract URL is resolved to a concrete package URL, and the request is forwarded to the handler for the concrete URL; but since the RequestProcessor is registered on the root URL "/", the RequestProcessor is called again, and must then dispatch the request to the static-content handler servlet explicitly by name ("default"). This is necessitated by the internals of the Servlet spec, specifically, because you can't call getNamedDispatcher with a URL parameter like you can with getRequestDispatcher.
Under the hood, a request for /path/page is handled like this:
ad_conn for in Tcl is
a method in either ACSSession, ACSRequest, or ACSPackage. Most of the
routines for getting information about the currently logged-in user
are available through the ACSSession class, and information about the
URL they requested is available through the ACSRequest class.
The current ACSSession, ACSRequest, and ACSPackage objects are all
in .ajp pages through fields of the acs object, declared
in the acs.jspi file that all .ajp pages should include. The current
ACSSession object is available through the acs.session
object, or in Java library code through ACSSession.get().
The current ACSRequest is similarly available through
acs.request and ACSRequest.get().
| Tcl/AOLserver | Java/JSP | ||
|---|---|---|---|
ad_conn user_id
| |||
ad_conn session_id
| |||
ad_secure_conn_p
| |||
ad_conn url
| |||
ad_conn extra_url
| |||
ad_set_client_property -secure_p t $module $name $value
|
acs.pkg object, of type
com.arsdigita.acs.ACSPackage. This supports methods for
getting information about an installed package instance; package
parameters that are available in Tcl through ad_parameter
are available through methods of the ACSPackage class as well.
Information about the requested site node is available through ACSRequest instead.
Examples:
| Method | Tcl/AOLserver | Java/JSP | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Get current package key. | ad_conn package_key
|
ACS.getKernelPackage(). Also, getParameter will attempt
to convert the parameter value into an int or
boolean if a variable of that type is supplied as the
default value.
Permissions class has two static methods,
hasPermission and requirePermission, that
correspond to the Tcl procedures ad_permission_p and
ad_require_permission. Both take an object ID, a role,
and return whether the logged-in user has the specified role on the
object.
public static boolean hasPermission(BigDecimal objectId,
String privilige)
throws SQLException;
public static boolean hasPermission(BigDecimal objectId,
BigDecimal userId,
String privilege)
throws SQLException;
public static void requirePermission(BigDecimal objectId,
String privilege)
throws SQLException, ACSException;
The requirePermission method terminates the execution of the calling page displays a message to the user when they do not have the specified permission. The user is redirected to the the registration page when not logged in.
In ACS Tcl, VUHs are just Tcl pages with a .vuh extension; VUHs are only served if there is no concrete matching file for a URL request and the different extension helps prevent a VUH from accidentally being served when it shouldn't be.
By placing a VUH at a particular URL, it will handle all incoming request URLs that start with their own URL, for which there is no matching concrete file. index.vuh is special because it will match any request in the directory. FFor example, /packages/foo/bar.vuh will handle a requests for /foo/bar/baz, but not for /foo/quux. But /packages/foo/index.vuh will handle any incoming URL that starts with /foo.
VUHs in ACS Java work identically to the way they work in ACS Tcl;
VUHs in ACS Java are JSP pages, with an extension of
.vuh.jsp (or .vuh.ajp). The
acs.request.getExtraUrl() method is often useful when
writing VUHs.
Context bars
To make a context bar in ACS Java, use the ContextBar
class. Due to Java's lack of variable-length argument lists, each
individual URL/name pair must be added with another procedure call.
| Tcl/AOLserver | Java/JSP |
|---|---|
set foo [ad_context_bar [list "foo" "foo page"] "bar"]
|
Procedures are scheduled by calling the schedule and
scheduleOnce methods, in various forms. Example:
| Tcl | Java |
|---|---|
|
|
name=value pairs; the
properties filename is given as the "acs.properties.file"
initialization parameter to the RequestProcessor servlet in the
web.xml deployment descriptor, or as a system property.
These propreties can accessed through the static methods of the Parameters class.
Parameters.get(String name, [, String default])
Parameters.getBoolean(String name, [, boolean default])
| Tcl | Java |
|---|---|
| |