ACS Java 4.0 provides analogs to functionality present in ACS Core 4.0. There
is a database API, a templating system, the ad_page_contract
validation tool, and identical data models. Once you learn the analogs of ACS
functionality in ACS Java, you should be able to port a package with ease.
cvs -d cvs.arsdigita.com:/usr/local/cvsroot checkout acs-packages/package-name
Apply a tag to mark the state of the code when you start. This ensures
that you are working against a stable target. If someone continues to
program the version you are porting, this won't affect your work. In your
local checkout
cd acs-packages/package-name
cvs tag port-begin-DATE
It is important to mark the date for clarity. Periodically, you should
check to make sure that the code is not being changed. You can do this by
cvs diff -c -r port-begin-20010116 -d TODAY
This activity is much like a code review. Your task is to understand what the package does, all of its requirements, and the design of the system that holds it together.
When performing this evaluation, be critical and inquisitive. Ask yourself if the package is doing everything it should efficiently. It is quite possible that it is not. This is how the process of porting is like review. You're likely to spot problems. Make a note of anything strange, and email the maintainer if appropriate. Even if you are the maintainer of the Tcl version, its worth rereading the code to look for problems.
Porting a package is a good opportunity to refactor the code. To refactor code is to apply a set of changes to the internal structure of the software to make it easier to understand and cheaper to modify without changing its observable behavior. Notice that this is very similar to the process of porting; both tasks involve altering the code without changing its observable behavior. When moving code from Tcl to Java, you need to understand what that behavior is and find the most efficient and maintainable means of expressing it in Java.
In some cases, you will want to create classes that represent state using member variables. Objects are nothing more than state with associated methods that provide functionality that require that state. Using objects provides a better means of organizing the variables and methods that provide functionality than relying solely on static methods.
For example, the ACSSession class encapsulates all of the session property information into one object. The session object is used as a means of organizing the functionality and the data structures associated with the object. This organization is more effective than a series of static methods that must maintain their own individual state and variables separate from each other. Several of the methods mutate the internal state as necessary, and others are static methods that don't require the state as appropriate.
| Tcl version | Java version |
|---|---|
| index.tcl
ad_page_contract {
Displays a list of available news items.
@param archive_p show archived news items?
@author Jon Salz (jsalz@mit.edu)
@author Christian Brechbuehler (christian@arsdigita.com)
@creation-date 11 Aug 2000
@cvs-id porting-guide.html,v 1.3 2001/02/26 20:41:05 bquinn Exp
} {
} -properties {
context_bar:onevalue
package_id:onevalue
subsite:multirow
item:multirow
admin_p:onevalue
}
set context_bar [ad_context_bar]
set package_id [ad_conn package_id]
set admin_p [ad_permission_p $package_id admin]
db_multirow item news_items_select {
select news_item_id, title
from news_items_obj
where context_id = :package_id
and sysdate >= release_date
and (expiration_date is null or expiration_date > sysdate)
}
ad_return_template
| index.ajp
<%@ include file="/acs.jspi" %>
<%
ad_page_contract {
Displays a list of available news items.
@param archive_p show archived news items?
@author Jon Salz (jsalz@mit.edu)
@author Christian Brechbuehler (christian@arsdigita.com)
@creation-date 11 Aug 2000
@cvs-id porting-guide.html,v 1.3 2001/02/26 20:41:05 bquinn Exp
} -properties {
context_bar:onevalue
package_id:onevalue
item:multirow
admin_p:onevalue
}
String admin_p = 0;
ContextBar context_bar = new ContextBar();
BigDecimal package_id = acs.pkg.getPackageId();
if (Permissions.hasPermission(package_id, acs.session.getUserId(), "admin")) {
admin_p = "1";
}
db_multirow item {
select news_item_id, title
from news_items_obj
where context_id = :package_id
and sysdate >= release_date
and (expiration_date is null or expiration_date > sysdate)
}
ad_return_template
%>
|
<@ include file="/acs.jspi"%> line.The template used by these files does not need to be changed.
| Tcl version | Java version |
|---|---|
| item-view.tcl
ad_page_contract {
View a news item.
@author Jon Salz (jsalz@arsdigita.com)
@author Christian Brechbuehler (christian@arsdigita.com)
@creation-date 11 Aug 2000
@cvs-id porting-guide.html,v 1.3 2001/02/26 20:41:05 bquinn Exp
} {
news_item_id:integer,notnull
} -properties {
body:onevalue
release_date:onevalue
title:onevalue
context_bar:onevalue
}
db_1row news_item_select {
select * from news_items where news_item_id = :news_item_id
}
set context_bar [ad_context_bar $title]
ad_return_template
| item-view.ajp
<%@ include file="/acs.jspi" %>
<%
ad_page_contract {
View a news item.
@author Jon Salz (jsalz@arsdigita.com)
@author Christian Brechbuehler (christian@arsdigita.com)
@creation-date 11 Aug 2000
@cvs-id porting-guide.html,v 1.3 2001/02/26 20:41:05 bquinn Exp
} {
news_item_id:integer,notnull
} -properties {
body:onevalue
release_date:onevalue
title:onevalue
context_bar:onevalue
}
db_1row news_item_select {
select title, body, release_date from news_items
where news_item_id = :news_item_id
}
ContextBar context_bar = new ContextBar();
context_bar.add(title.toString());
release_date = Utilities.toPrettyDate(release_date.toString());
ad_return_template
%>
|
| Tcl version | Java version |
|---|---|
admin/index.tcl
ad_page_contract {
News main administration page.
@author Jon Salz (jsalz@arsdigita.com)
@creation-date 11 Aug 2000
@cvs-id porting-guide.html,v 1.3 2001/02/26 20:41:05 bquinn Exp
} {
} -properties {
context_bar:onevalue
item:multirow
}
set context_bar [ad_context_bar]
set package_id [ad_conn package_id]
db_multirow item news_items_select {
select news_item_id, title
from news_items_obj
where context_id = :package_id
}
ad_return_template
|
admin/index.ajp
<%@ include file="/acs.jspi" %>
<%
ad_page_contract {
News main administration page.
@author Jon Salz (jsalz@arsdigita.com)
@creation-date 11 Aug 2000
@cvs-id porting-guide.html,v 1.3 2001/02/26 20:41:05 bquinn Exp
} -properties {
context_bar:onevalue
item:multirow
}
ContextBar context_bar = new ContextBar();
BigDecimal package_id = acs.pkg.getPackageId();
db_multirow item {
select news_item_id, title
from news_items_obj
where context_id = :package_id
}
ad_return_template
%>
|
| Tcl version | Java version |
|---|---|
admin/item-new.tcl
ad_page_contract {
Form to add an item.
@author Jon Salz (jsalz@arsdigita.com)
@author Christian Brechbuehler (brech@arsdigita.com)
@creation-date 11 Aug 2000
@cvs-id porting-guide.html,v 1.3 2001/02/26 20:41:05 bquinn Exp
} -properties {
context_bar:onevalue
news_item_id:onevalue
} -query {
}
set context_bar [ad_context_bar "Add an Item"]
set news_item_id [db_nextval acs_object_id_seq]
ad_return_template
| admin/item-new.ajp
<%@ include file="/acs.jspi" %>
<%
ad_page_contract {
Form to add an item.
@author Jon Salz (jsalz@arsdigita.com)
@author Christian Brechbuehler (brech@arsdigita.com)
@creation-date 11 Aug 2000
@cvs-id porting-guide.html,v 1.3 2001/02/26 20:41:05 bquinn Exp
} -properties {
release_date_widget:onevalue
expiration_date_widget:onevalue
context_bar:onevalue
news_item_id:onevalue
}
ContextBar context_bar = new ContextBar();
context_bar.add("Add an Item");
BigDecimal news_item_id = [db_nextval acs_object_id_seq];
String release_date_widget = com.arsdigita.acs.html.HtmlWidgets.dateWidget("release_date");
String expiration_date_widget = com.arsdigita.acs.html.HtmlWidgets.dateWidget("expiration_date");
ad_return_template
%>
|
<tr>
<th align=right>Release Date:</th>
<td><%=[ad_dateentrywidget release_date]%></td>
</tr>
<tr>
<th align=right>Expiration Date:</th>
<td><%=[ad_dateentrywidget expiration_date ""]%></td>
</tr>
There is Tcl code included in the template. Naturally, this cannot be ported,
and the ACS Tcl Templating guide reccomends against doing this. The version for
ACS Java thus constitutes a refactoring of the code. Tcl code that was in the
presentation layer got moved into Java. Here is the ACS Java version of the
relevant portion of the template:
<tr>
<th align=right>Release Date:</th>
<td>@release_date_widget@</td>
</tr>
<tr>
<th align=right>Expiration Date:</th>
<td>@expiration_date_widget@</td>
</tr>
| Tcl version | Java version |
|---|---|
admin/item-new-2.tcl
ad_page_contract {
Post a new news item.
@author Jon Salz (jsalz@arsdigita.com)
@creation-date 11 Aug 2000
@cvs-id porting-guide.html,v 1.3 2001/02/26 20:41:05 bquinn Exp
} {
news_item_id:integer,notnull
title:notnull
body:notnull
release_date:date,array,notnull
expiration_date:date,array
}
set release_date_str $release_date(date)
if { [info exists expiration_date(date)] } {
set expiration_date_str $expiration_date(date)
} else {
set expiration_date_str ""
}
set package_id [ad_conn package_id]
db_dml item_insert {
declare
news_item_id integer;
begin
news_item_id := acs_object.new(object_id => :news_item_id,
object_type => 'news_item',
context_id => :package_id);
insert into news_items(news_item_id, title, body, release_date, expiration_date)
values(:news_item_id, :title, :body, :release_date_str, :expiration_date_str);
end;
}
ad_returnredirect "index"
| admin/item-new-2.ajp
<%@ include file="/acs.jspi" %>
<%
ad_page_contract {
Post a new news item.
@author Jon Salz (jsalz@arsdigita.com)
@creation-date 11 Aug 2000
@cvs-id porting-guide.html,v 1.3 2001/02/26 20:41:05 bquinn Exp
} {
news_item_id:integer,notnull
title:notnull
body:notnull,html
release_date:date,notnull
expiration_date:date
}
BigDecimal package_id = acs.pkg.getPackageId();
db_dml item_insert {
declare
news_item_id integer;
begin
news_item_id := acs_object.new(object_id => :news_item_id,
object_type => 'news_item',
context_id => :package_id);
insert into news_items(news_item_id, title, body, release_date, expiration_date)
values(:news_item_id, :title, :body, :release_date, :expiration_date);
end;
}
Utilities.sendRedirect(response, "index");
%>
|
There are a few more files in the news package, but the porting of them is obvious given these examples. These examples illustrate how easy it can be to port packages to ACS Java that are primarily pages. Porting packages that exist more of libraries of code require more thought out refactorings beyond the scope of this document to provide.
It is interesting to note that most of the pages associated with the News application exist simply as wrappers to accept input from the user and use it as variables in a block of SQL or DML. The task of porting the application requires enduring a large amount of tedious and error prone work. When you find yourself working through a lot of tedious work, ask yourself the question, is there a better way? There probably is, and if you can find it, you can accomplish some solid engineering. This is the essence of good refactoring and software design. Be on the lookout for such opportunities throughout the task of porting the applications.
| Document Revision # | Action Taken, Notes | When? | By Whom? |
|---|---|---|---|
| 0.1 | Creation | January 16, 2001 | Bryan Quinn |