diff options
author | R. Tyler Ballance <tyler@monkeypox.org> | 2009-11-16 21:09:13 -0800 |
---|---|---|
committer | R. Tyler Ballance <tyler@monkeypox.org> | 2009-11-16 21:09:13 -0800 |
commit | d9ce7916e309e2393d824e249f512d2629e5e181 (patch) | |
tree | 6b7ad5cd6292f6e017e048fbeb4551facbabd174 /docs/users_guide_src/webware.tex | |
parent | e43765a679b84c52df875e9629d303e304af50a1 (diff) | |
download | python-cheetah-d9ce7916e309e2393d824e249f512d2629e5e181.tar.gz |
Revert "Delete the "old" docs directory to make way for fancy smancy sphinx"docs
This reverts commit 5dc95cfcd015628665d3672e56d0551943b5db6b.
Diffstat (limited to 'docs/users_guide_src/webware.tex')
-rwxr-xr-x | docs/users_guide_src/webware.tex | 575 |
1 files changed, 575 insertions, 0 deletions
diff --git a/docs/users_guide_src/webware.tex b/docs/users_guide_src/webware.tex new file mode 100755 index 0000000..a5702d8 --- /dev/null +++ b/docs/users_guide_src/webware.tex @@ -0,0 +1,575 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Using Cheetah with Webware} +\label{webware} + +{\bf Webware for Python} is a 'Python-Powered Internet Platform' that runs +servlets in a manner similar to Java servlets. {\bf WebKit} is the name of +Webware's application server. For more details, please visit +\url{http://webware.sourceforge.net/}. + +All comments below refer to the official version of Webware, the DamnSimple! +offshoot at ?, and the now-abandoned WebwareExperimental implementation at +\url{http://sourceforge.net/projects/expwebware/}, except where noted. All the +implementations are 95\% identical to the servlet writer: their differences lie +in their internal structure and configuration files. One difference is that +the executable you run to launch standard Webware is called \code{AppServer}, +whereas in WebwareExperimental it's called \code{webkit}. But to servlets +they're both "WebKit, Webware's application server", so it's one half dozen to +the other. In this document, we generally use the term {\bf WebKit} to refer +to the currently-running application server. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Installing Cheetah on a Webware system} +\label{webware.installing} + +Install Cheetah after you have installed Webware, following the instructions in +chapter \ref{gettingStarted}. + +The standard Cheetah test suite ('cheetah test') does not test Webware features. +We plan to build a test suite that can run as a Webware servlet, containing +Webware-specific tests, but that has not been built yet. In the meantime, you +can make a simple template containing something like "This is a very small +template.", compile it, put the *.py template module in a servlet +directory, and see if Webware serves it up OK. + +{\em You must not have a Webware context called "Cheetah".} If you do, Webware +will mistake that directory for the Cheetah module directory, and all +template-servlets will bomb out with a "ImportError: no module named Template". +(This applies only to the standard Webware; WebwareExperimental does not have +contexts.) + +If Webware complains that it cannot find your servlet, make sure +'.tmpl' is listed in 'ExtensionsToIgnore' in your 'Application.config' file. + +% @@MO: Should explain extension cascading and how without it, standard +% Webware pretends a file doesn't exist if it finds two or more servable files +% that match the URL. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Containment vs Inheritance} +\label{webware.background} + +Because Cheetah's core is flexible, there are many ways to integrate it with +Webware servlets. There are two broad strategies: the {\bf Inheritance +approach} and the {\bf Containment approach}. The difference is +that in the Inheritance approach, your template object \code{\em is} the +servlet, whereas in the Containment approach, the servlet is not a template but +merely {\em uses} template(s) for portion(s) of its work. + +The Inheritance approach is recommended for new sites because it's simpler, and +because it scales well for large sites with a +site->section->subsection->servlet hierarchy. The Containment approach is +better for existing servlets that you don't want to restructure. For instance, +you can use the Containment approach to embed a discussion-forum table at the +bottom of a web page. + +However, most people who use Cheetah extensively seem +to prefer the Inheritance approach because even the most analytical servlet +needs to produce {\em some} output, and it has to fit the site's look and feel +{\em anyway}, so you may as well use a template-servlet as the place to put the +output. Especially since it's so easy to add a template-servlet to a site once +the framework is established. So we recommend you at least evaluate the +effort that would be required to convert your site framework to template +superclasses as described below, vs the greater flexibility and manageability +it might give the site over the long term. You don't necessarily have to +convert all your existing servlets right away: just build common site templates +that are visually and behaviorally compatible with your specification, and use +them for new servlets. Existing servlets can be converted later, if at all. + +Edmund Liam is preparing a section on a hybrid approach, in which the +servlet is not a template, but still calls template(s) in an inheritance +chain to produce the output. The advantage of this approach is that you +aren't dealing with \code{Template} methods and Webware methods in the +same object. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsubsection{The Containment Approach} +\label{webware.containment} + +In the Containment approach, your servlet is not a template. Instead, it +it makes its own arrangements to create and use template object(s) for whatever +it needs. The servlet must explicitly call the template objects' +\code{.respond()} (or \code{.\_\_str\_\_()}) method each time it needs to fill +the template. This does not present the output to the user; it merely gives +the output to the servlet. The servlet then calls its +\code{\#self.response().write()} method to send the output to the user. + +The developer has several choices for managing her templates. She can store the +template definition in a string, file or database and call +\code{Cheetah.Template.Template} manually on it. Or she can put the +template definition in a *.tmpl file and use {\bf cheetah compile} (section +\ref{howWorks.cheetah-compile}) to convert it to a Python class in a *.py +module, and then import it into her servlet. + +Because template objects are not thread safe, you should not store one +in a module variable and allow multiple servlets to fill it simultaneously. +Instead, each servlet should instantiate its own template object. Template +{\em classes}, however, are thread safe, since they don't change once created. +So it's safe to store a template class in a module global variable. + +% @@MO: Example of containment. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsubsection{The Inheritance Approach} +\label{webware.inheritance} + +In the Inheritance approach, your template object doubles as as Webware +servlet, thus these are sometimes called {\bf template-servlets}. {\bf cheetah +compile} (section \ref{howWorks.cheetah-compile}) automatically creates modules +containing valid Webware servlets. A servlet is a subclass of Webware's +\code{WebKit.HTTPServlet} class, contained in a module with the same name as +the servlet. WebKit uses the request URL to find the module, and then +instantiates the servlet/template. The servlet must have a \code{.respond()} +method (or \code{.respondToGet()}, \code{.respondToPut()}, etc., but the +Cheetah default is \code{.respond()}). Servlets created by \code{cheetah +compile} meet all these requirements. + +(Cheetah has a Webware plugin that automatically converts a \code{.tmpl servlet +file} into a \code{.py servlet file} when the \code{.tmpl servlet file} is +requested by a browser. However, that plugin is currently unavailable because +it's being redesigned. For now, use \code{cheetah compile} instead.) + +What about logic code? Cheetah promises to keep content (the placeholder +values), graphic design (the template definition and is display logic), and +algorithmic logic (complex calculations and side effects) separate. How? +Where do you do form processing? + +The answer is that your template class can inherit from a pure Python class +containing the analytical logic. You can either use the \code{\#extends} +directive in Cheetah to indicate the superclass(es), or write a Python +\code{class} statement to do the same thing. See the template +\code{Cheetah.Templates.SkeletonPage.tmpl} and its pure Python class +\code{Cheetah.Templates.\_SkeletonPage.py} for an example of a template +inheriting logic code. (See sections \ref{inheritanceEtc.extends} and +\ref{inheritanceEtc.implements} for more information about \code{\#extends} and +\code{\#implements}. They have to be used a certain right way.) + +If \code{\#WebKit.HTTPServlet} is not available, Cheetah fakes it with a +dummy class to satisfy the dependency. This allows servlets to be tested on +the command line even on systems where Webware is not installed. This works +only with servlets that don't call back into WebKit for information about the +current web transaction, since there is no web transaction. Trying to access +form input, for instance, will raise an exception because it depends on a +live web request object, and in the dummy class the request object is +\code{None}. + +Because Webware servlets must be valid Python modules, and ``cheetah compile'' +can produce only valid module names, if you're converting an existing site that +has .html filenames with hyphens (-), extra dots (.), etc, you'll have to +rename them (and possibly use redirects). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Site frameworks} +\label{webware.siteFrameworks} + +Web sites are normally arranged hierarchically, with certain features common +to every page on the site, other features common to certain sections or +subsections, and others unique to each page. You can model this easily with +a hierarchy of classes, with specific servlets inheriting from their more +general superclasses. Again, you can do this two ways, using Cheetah's +{\bf Containment} approach or {\bf Inheritance} approach. + +In the Inheritance approach, parents provide \code{\#block}s and children +override them using \code{\#def}. Each child \code{\#extend}s its immediate +parent. Only the leaf servlets need to be under WebKit's document root +directory. The superclass servlets can live anywhere in the filesystem +that's in the Python path. (You may want to modify your WebKit startup +script to add that library directory to your \code{PYTHONPATH} before starting +WebKit.) + +% @@MO Examples: simple, IronSite, SkeletonPage. + +Section \ref{libraries.templates.skeletonPage} contains information on a stock +template that simplifies defining the basic HTML structure of your web +page templates. + +In the Containment approach, your hierarchy of servlets are not templates, but +each uses one or more templates as it wishes. Children provide callback +methods to to produce the various portions of the page that are their +responsibility, and parents call those methods. Webware's \code{WebKit.Page} +and \code{WebKit.SidebarPage} classes operate like this. + +% @@MO Show examples of WebKit.Page and WebKit.SidebarPage. + +Note that the two approaches are not compatible! \code{WebKit.Page} was not +designed to intermix with \code{Cheetah.Templates.SkeletonPage}. Choose either +one or the other, or expect to do some integration work. + +If you come up with a different strategy you think is worth noting in this +chapter, let us know. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Directory structure} +\label{webware.directoryStructure} + +Here's one way to organize your files for Webware+Cheetah. + +\begin{verbatim} +www/ # Web root directory. + site1.example.com/ # Site subdirectory. + apache/ # Web server document root (for non-servlets). + www/ # WebKit document root. + index.py # http://site1.example.com/ + index.tmpl # Source for above. + servlet2.py # http://site1.example.com/servlet2 + servlet2.tmpl # Source for above. + lib/ # Directory for helper classes. + Site.py # Site superclass ("#extends Site"). + Site.tmpl # Source for above. + Logic.py # Logic class inherited by some template. + webkit.config # Configuration file (for WebwareExperimental). + Webware/ # Standard Webware's MakeAppWorkDir directory. + AppServer # Startup program (for standard Webware). + Configs/ # Configuration directory (for standard Webware). + Application.config + # Configuration file (for standard Webware). + site2.example.org/ # Another virtual host on this computer.... +\end{verbatim} + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Initializing your template-servlet with Python code} +\label{webware.calculations} + +If you need a place to initialize variables or do calculations for your +template-servlet, you can put it in an \code{.awake()} method because WebKit +automatically calls that early when processing the web transaction. If you +do override \code{.awake()}, be sure to call the superclass \code{.awake} +method. You probably want to do that first so that you have access to the +web transaction data \code{Servlet.awake} provides. You don't have to worry +about whether your parent class has its own \code{.awake} method, just call +it anyway, and somebody up the inheritance chain will respond, or at minimum +\code{Servlet.awake} will respond. Section +\ref{tips.callingSuperclassMethods} gives examples of how to call a +superclass method. + +As an alternative, you can put all your calculations in your own method and +call it near the top of your template. (\code{\#silent}, section +\ref{output.silent}). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Form processing} +\label{webware.form} + +There are many ways to display and process HTML forms with Cheetah. +But basically, all form processing involves two steps. +\begin{enumerate} +\item{} Display the form. +\item{} In the next web request, read the parameters the user submitted, +check for user errors, perform any side effects (e.g., reading/writing a +database or session data) and present the user an HTML response or another +form. +\end{enumerate} + +The second step may involve choosing between several templates to fill (or +several servlets to redirect to), or a big if-elif-elif-else construct to +display a different portion of the template depending on the situation. + +In the oldest web applications, step 1 and step 2 were handled by separate +objects. Step 1 was a static HTML file, and step 2 was a CGI script. +Frequently, a better strategy is to have a single servlet handle both steps. +That way, the servlet has better control over the entire situation, and if +the user submits unacceptable data, the servlet can redisplay the form with a +"try again" error message at the top and and all the previous input filled in. +The servlet can use the presence or absence of certain CGI parameters (e.g., +the submit button, or a hidden mode field) to determine which step to take. + +One neat way to build a servlet that can handle both the form displaying and +form processing is like this: + +\begin{enumerate} +\item Put your form HTML into an ordinary template-servlet. In each input + field, use a placeholder for the value of the \code{VALUE=} attribue. + Place another placeholder next to each field, for that field's error + message. +\item Above the form, put a \code{\$processFormData} method call. +\item Define that method in a Python class your template \code{\#extend}s. (Or + if it's a simple method, you can define it in a \code{\#def}.) The method + should: + \begin{enumerate} + \item Get the form input if any. + \item If the input variable corresponding to the submit field is empty, + there is no form input, so we're showing the form for the first time. + Initialize all VALUE= variables to their default value (usually ""), + and all error variables to "". Return "", which will be the value for + \code{\$processFormData}. + \item If the submit variable is not empty, fill the VALUE= variables with + the input data the user just submitted. + \item Now check the input for errors and put error messages in the error + placeholders. + \item If there were any user errors, return a general error message + string; this will be the value for \code{\$processFormData}. + \item If there were no errors, do whatever the form's job is (e.g., update + a database) and return a success message; this will be the value for + \code{\$processFormData}. + \end{enumerate} +\item The top of the page will show your success/failure message (or nothing +the first time around), with the form below. If there are errors, the user +will have a chance to correct them. After a successful submit, the form will +appear again, so the user can either review their entry, or change it and +submit it again. Depending on the application, this may make the servlet +update the same database record again, or it may generate a new record. +\end{enumerate} + +% @@MO: Example of a template that shows a form and then processes the input. + +\code{FunFormKit} is a third-party Webware package that makes it easier to +produce forms and handle their logic. It has been successfully been used with +Cheetah. You can download FunFormKit from +\url{http://colorstudy.net/software/funformkit/} and try it out for yourself. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Form input, cookies, session variables and web server variables} +\label{webware.input} + +General variable tips that also apply to servlets are in section +\ref{tips.placeholder}. + +To look up a CGI GET or POST parameter (with POST overriding): +\begin{verbatim} +$request.field('myField') +self.request().field('myField') +\end{verbatim} +These will fail if Webware is not available, because \code{\$request} +(aka \code{self.request()} will be \code{None} rather than a Webware +\code{WebKit.Request} object. If you plan to read a lot of CGI parameters, +you may want to put the \code{.fields} method into a local variable for +convenience: +\begin{verbatim} +#set $fields = $request.fields +$fields.myField +\end{verbatim} +But remember to do complicated calculations in Python, and assign the results +to simple variables in the searchList for display. These \code{\$request} +forms are useful only for occasions where you just need one or two simple +request items that going to Python for would be overkill. + +To get a cookie or session parameter, subsitute ``cookie'' or ``session'' for +``field'' above. To get a dictionary of all CGI parameters, substitute +``fields'' (ditto for ``cookies''). To verify a field exists, +substitute ``hasField'' (ditto for ``hasCookie''). + +Other useful request goodies: +\begin{verbatim} +## Defined in WebKit.Request +$request.field('myField', 'default value') +$request.time ## Time this request began in Unix ticks. +$request.timeStamp ## Time in human-readable format ('asctime' format). +## Defined in WebKit.HTTPRequest +$request.hasField.myField ## Is a CGI parameter defined? +$request.fields ## Dictionary of all CGI parameters. +$request.cookie.myCookie ## A cookie parameter (also .hasCookie, .cookies). +$request.value.myValue ## A field or cookie variable (field overrides) + ## (also .hasValue). +$request.session.mySessionVar # A session variable. +$request.extraURLPath ## URL path components to right of servlet, if any. +$request.serverDictionary ## Dict of environmental vars from web server. +$request.remoteUser ## Authenticated username. HTTPRequest.py source + ## suggests this is broken and always returns None. +$request.remoteAddress ## User's IP address (string). +$request.remoteName ## User's domain name, or IP address if none. +$request.urlPath ## URI of this servlet. +$request.urlPathDir ## URI of the directory containing this servlet. +$request.serverSidePath ## Absolute path of this servlet on local filesystem. +$request.serverURL ## URL of this servlet, without "http://" prefix, + ## extra path info or query string. +$request.serverURLDir ## URL of this servlet's directory, without "http://". +$log("message") ## Put a message in the Webware server log. (If you + ## define your own 'log' variable, it will override + ## this; use $self.log("message") in that case. +\end{verbatim} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsubsection{.webInput()} +\label{webware.webInput} + +From the method docstring: + +\begin{verbatim} + def webInput(self, names, namesMulti=(), default='', src='f', + defaultInt=0, defaultFloat=0.00, badInt=0, badFloat=0.00, debug=False): + +This method places the specified GET/POST fields, cookies or session variables +into a dictionary, which is both returned and put at the beginning of the +searchList. It handles: + * single vs multiple values + * conversion to integer or float for specified names + * default values/exceptions for missing or bad values + * printing a snapshot of all values retrieved for debugging +All the 'default*' and 'bad*' arguments have "use or raise" behavior, meaning +that if they're a subclass of Exception, they're raised. If they're anything +else, that value is substituted for the missing/bad value. + +The simplest usage is: + + #silent $webInput(['choice']) + $choice + + dic = self.webInput(['choice']) + write(dic['choice']) + +Both these examples retrieves the GET/POST field 'choice' and print it. If you +leave off the "#silent", all the values would be printed too. But a better way +to preview the values is + + #silent $webInput(['name'], $debug=1) + +because this pretty-prints all the values inside HTML <PRE> tags. + +Since we didn't specify any coversions, the value is a string. It's a "single" +value because we specified it in 'names' rather than 'namesMulti'. Single +values work like this: + * If one value is found, take it. + * If several values are found, choose one arbitrarily and ignore the rest. + * If no values are found, use or raise the appropriate 'default*' value. + +Multi values work like this: + * If one value is found, put it in a list. + * If several values are found, leave them in a list. + * If no values are found, use the empty list ([]). The 'default*' + arguments are *not* consulted in this case. + +Example: assume 'days' came from a set of checkboxes or a multiple combo box +on a form, and the user chose "Monday", "Tuesday" and "Thursday". + + #silent $webInput([], ['days']) + The days you chose are: #slurp + #for $day in $days + $day #slurp + #end for + + dic = self.webInput([], ['days']) + write("The days you chose are: ") + for day in dic['days']: + write(day + " ") + +Both these examples print: "The days you chose are: Monday Tuesday Thursday". + +By default, missing strings are replaced by "" and missing/bad numbers by zero. +(A "bad number" means the converter raised an exception for it, usually because +of non-numeric characters in the value.) This mimics Perl/PHP behavior, and +simplifies coding for many applications where missing/bad values *should* be +blank/zero. In those relatively few cases where you must distinguish between +""/zero on the one hand and missing/bad on the other, change the appropriate +'default*' and 'bad*' arguments to something like: + * None + * another constant value + * $NonNumericInputError/self.NonNumericInputError + * $ValueError/ValueError +(NonNumericInputError is defined in this class and is useful for +distinguishing between bad input vs a TypeError/ValueError +thrown for some other reason.) + +Here's an example using multiple values to schedule newspaper deliveries. +'checkboxes' comes from a form with checkboxes for all the days of the week. +The days the user previously chose are preselected. The user checks/unchecks +boxes as desired and presses Submit. The value of 'checkboxes' is a list of +checkboxes that were checked when Submit was pressed. Our task now is to +turn on the days the user checked, turn off the days he unchecked, and leave +on or off the days he didn't change. + + dic = self.webInput([], ['dayCheckboxes']) + wantedDays = dic['dayCheckboxes'] # The days the user checked. + for day, on in self.getAllValues(): + if not on and wantedDays.has_key(day): + self.TurnOn(day) + # ... Set a flag or insert a database record ... + elif on and not wantedDays.has_key(day): + self.TurnOff(day) + # ... Unset a flag or delete a database record ... + +'source' allows you to look up the variables from a number of different +sources: + 'f' fields (CGI GET/POST parameters) + 'c' cookies + 's' session variables + 'v' "values", meaning fields or cookies + +In many forms, you're dealing only with strings, which is why the +'default' argument is third and the numeric arguments are banished to +the end. But sometimes you want automatic number conversion, so that +you can do numeric comparisons in your templates without having to +write a bunch of conversion/exception handling code. Example: + + #silent $webInput(['name', 'height:int']) + $name is $height cm tall. + #if $height >= 300 + Wow, you're tall! + #else + Pshaw, you're short. + #end if + + dic = self.webInput(['name', 'height:int']) + name = dic[name] + height = dic[height] + write("%s is %s cm tall." % (name, height)) + if height > 300: + write("Wow, you're tall!") + else: + write("Pshaw, you're short.") + +To convert a value to a number, suffix ":int" or ":float" to the name. The +method will search first for a "height:int" variable and then for a "height" +variable. (It will be called "height" in the final dictionary.) If a numeric +conversion fails, use or raise 'badInt' or 'badFloat'. Missing values work +the same way as for strings, except the default is 'defaultInt' or +'defaultFloat' instead of 'default'. + +If a name represents an uploaded file, the entire file will be read into +memory. For more sophisticated file-upload handling, leave that name out of +the list and do your own handling, or wait for Cheetah.Utils.UploadFileMixin. + +This mixin class works only in a subclass that also inherits from +Webware's Servlet or HTTPServlet. Otherwise you'll get an AttributeError +on 'self.request'. + +EXCEPTIONS: ValueError if 'source' is not one of the stated characters. +TypeError if a conversion suffix is not ":int" or ":float". +\end{verbatim} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{More examples} +\label{webware.examples} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsubsection*{Example A -- a standalone servlet} +%\label{} + +% @@MO: + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsubsection*{Example B -- a servlet under a site framework} +%\label{} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsubsection*{Example C -- several servlets with a common template} +%\label{} + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Other Tips} +\label{webware.otherTips} + +If your servlet accesses external files (e.g., via an \code{\#include} +directive), remember that the current directory is not necessarily directory +the servlet is in. It's probably some other directory WebKit chose. To find a +file relative to the servlet's directory, prefix the path with whatever +\code{self.serverSidePath()} returns (from \code{Servlet.serverSidePath()}. + +If you don't understand how \code{\#extends} and \code{\#implements} work, and +about a template's main method, read the chapter on inheritance (sections +\ref{inheritanceEtc.extends} and \ref{inheritanceEtc.implements}). This may +help you avoid buggy servlets. + +% Local Variables: +% TeX-master: "users_guide" +% End: +%# vim: sw=4 ts=4 expandtab |