Cocoon Wiki Contents | ![]() |
Showing off Forrest's Wiki parsing
- ApacheModProxy
- AuthWithTomcat
-
BeginnerInstallTomcatUnix
- installing tomcat in 15 minutes (cookbook approach for unix/linux)
- What you will get from this page
- Your basic skills
- Technical prerequisites
- Links to other information sources
- Step I: Get the distrib
- Step II: Unpack it
- Step III: Prepare for startup
- modify the standard port settings
- Start your container for the first time
- Verify operation
- Where to go next
- First aid if something goes wrong.
- Look at the startup logs
- page metadata
-
BeginnerInstallTomcatWindows
- installing tomcat in 15 minutes (cookbook approach for windows)
- What you will get from this page
- Your basic skills
- Technical prerequisites
- Links to other information sources
- Step I: Get the distrib
- Step II: Unpack it (if you use the zip-distrib)
- install the installshield based exe-version
- Step III: Prepare for startup (only zip-distrib)
- modify the standard port settings
- Start your container for the first time
- Verify operation
- Where to go next
- First aid if something goes wrong.
- Look at the startup logs
- page metadata
-
BeginnerSimpleWebappOrganisation
- Organising your development (cookbook)
- What you will get from this page
- Your basic skills
- Technical prerequisites
- Links to other information sources
- The problem: Organisational complexity
- The solution: Separating your work by use of subsitemaps
- Part I: Use the existing possibilities
- Part II: Connecting your "work" folder to Cocoon
- What did we achieve?
- Advanced issue: separating your work folder from Cocoon
- Appendix
- page metadata
-
BlocksDefinition
- Part 1: introduction
- Are we really cloning the servlet API?
- Applying Avalon COP philosophy over again
- Improvements
- Improvement #2: polymorphic behavior
- Improvement #3: block inheritance
- Part 2: technical details
- Cocoon Blocks
- Possible use-case scenario
- Resource dereferencing
- Dereferencing navigation
- Some design decision taken
- VERSIONING AS PART OF THE BEHAVIOR URI
- CROSS-BLOCK SECURITY
- COCOON MANAGER SECURITY
- OPTIONAL COP
- Conclusions
- THANKS
- CODA
- TODO
- The block manager should present the user with a form on how to configure the block
- Which avalon container should we use?
- How do we implement the block manager?
- The 'uber library of cocoon blocks'
- Should we "digitally sign" our blocks? if so, how?
- Reader's comments
- Question on versioning
- How to do a first microstep towards the goal.
-
CommandLine
- The Cocoon Command Line Interface
- 1. Using the Cocoon Command Line Interface
- 2. Command line parameters
- 3. Directories
- 4. URIs and URI files
- 5. Following Links
- 6. Mime Type Checking
- 7. Multiple Page Renderings
- 8. Logging
- 9. Precompiling XSPs
- 10. Precompiling Sitemaps
- 11. Agent Options
- 12. Accept Options
- 13. Sample Unix Script
- 14. Sample Windows Batch File
- 15. Sample from Cocoon's own build.xml file
- Some Keywords
-
ConfiguringTheLogs
- Cocoon Logging Configuration
- Basics
- Configuration
- Declaring LogTargets
- Example
- AsyncLogTargetFactory
- Example
- CocoonTargetFactory
- Example
- DatagramTargetFactory
- FileTarget
- Example
- JDBCTargetFactory
- Example
- JMSTargetFactory
- PriorityFilterTargetFactory
- Example
- SMTPTargetFactory
- ServletTargetFactory
- Example
- SocketTargetFactory
- StreamTargetFactory
- Example
- Formatting
- PatternFormatter
- ExtendedPatternFormatter
- CocoonLogFormatter
- Logging Rotation
- Specifying logkit.xconf
- Examples
- See Also
- DistributingCocoonApplications
- EXistInCocoon
-
ForrestProposal
- Background
- Procedure
- Outline of requirements and issues
- 2. Forrest to automatically (or by manual trigger) generate the website at xml.apache.org/cocoon/
- 3. How will people who download just Cocoon, be able to locally build and work with its documentation? Do they need to install Forrest separately, or does Cocoon still build its own documentation using its own set of stylesheets?
- 4. Cocoon xdocs need to be transformed to document-v11 DTD. Forrest has stylesheets, DTDs, anttasks, and XML validation to assist with this.
- 5. After Cocoon xdocs are sucessfully transformed, the xdocs in CVS will be updated to be document-v11 type.
- 6. Simultaneously with #5, the Cocoon XSLT stylesheets, DTDs, and sitemaps will need to be updated to reflect the document-v11 from Forrest.
- 7. Expect a few people to help, especially during the transition of xdocs.
- 8. The documents that are published by Forrest need to be subsequently checked-in to the xml-site CVS.
- 9. Use a consistent version of Forrest during the transition phase.
- 10. Decide which "tabs" to use. See
- 11. Decide whether we want to show the "authors" of each document (which may not be relevant as it is a group effort).
- 12. The status documents (changes, todo) need to merge into status.xml
- 13. Elevate doc-related changes and todo documents to top-level status file.
- 14. Need a mechanism to enable existing and future (with any reorganization) redirect pages.
- 15. Do the old documnet-v10 DTDs and OASIS Catalog entries need to remain in Cocoon CVS? Other people may be still using them for their own projects.
- 16. Decide if Cocoon needs its own skin (to protect its identity/brand) before/shortly after transition, given the fact many sites are adopting the default skin and given the prospect of top-level project status.
- 17. Perhaps the navigation/organization of pages should be revisited at the same time.
- 18. Staging mechanism for quality control of website production
- 19. From which branch does the website get generated - head or release branch?
- Recent major changes
- Some relevant email discussion and documents
- To Do
- See also
- Abbreviated names of readers
- JBossDeployment
- Jars2exclude
- ModularDatabaseActions
- OlderNews
- OpenOfficeGeneration
- RhinoWithContinuations
- SeparationOfLogicAndContent
- TextFormattingRules
- WoodySample
- XMLFormXindice
- XMLFormXindiceOldVersion
- XSPSyntax
- index
ApacheModProxy
After few discussions had face-to-face with some of you (Stefanoon the phone ranting about setting up Tomcat, Jeremy over lunch at my place few weeks ago, and several others), and few odd questions popping out on the list, I feel the need to tell you why my vision is so narrow when someone touches the Apacheargument.
As I said several times in the past 6 years, I've learnt how to use Apache (1.3 first, and 2.0 lately) to suit my needs and I would never envision an HTTP server running without it.
Given my pragmaticalvision, it's hard to explain whyI am so biased, and probably the best way to come out-of-the-loophole is to share the few things I learnt, and that make my everyday life of administrator easy...
So, those are few tips for those of you who wonder about my rants.
----
Why Apache as a front end?
Probably the first and most important question to answer is WHY it is so important to have Apache HTTPd as a front-end for a website.
I believe that for anyone, there's nothing more annoying than hitting a web page, waiting for a few seconds, and then seeing our favorite browser coming up with "The connection was refused when attempting to contact".
In my opinion (and my boss') it is unacceptable to have a "downtime" on a website, and if that happens, whoever connects needs to know what's going on, or, at least, we need to tell him something: "We are sorry, but" sounds so much better (maybe with our little nice logo, and yada yada, yada).
When once I asked to Brian Behlendorf why Apache was doing some oddities in the code, he responded "Call it defensive programming": this explains the entire vision behind Apache: Apache, no matter what, can not"go down" and not respond to HTTP requests. This is the essence behind it and its design is centered around this idea, so, in my opinion (and experience) it is that one option allowing us to achieve our goal of "zero port 80 downtime".
Apache's design enforces a multi-process model: there is always a minimal wrapper bound to port 80 (as safe and minimal as possible), spawning new OS processes per request doing the work. This allows that even in the worst case scenario (a segmentation violation in the code that dumps the entire OS process), something will be sent back to the client.
A Java-based web server can not achieve this. Java is a single-process environment and if something happens to it, it will just exit, unbinding port 80 and leaving our clients with "connection refused".
There is another issue, important one, about security. Java does not support switching user-ID after it's started, and under UNIX operating systems, everyone knows that noone apart from rootcan bind to ports < 1024.
In our case it is a problem, I either decide to run my service as root (and that is NOT a good idea), or I bind to some port > 1024 (usually 8080). But then, the complexity arises when forwarding requests for port 80 (our usual HTTP service) to a port above 1024 (8080). Either firewall packages, or port remappers, any of those solution involves a some-degree of complexity.
Apache avoids all that. Being native, it can bind to ports < 1024 and run as a non-privileged user, allowing us to run our servlet container (as well) as a non privileged user.
But those are not the only advantages, Apache helps us in much much better ways, and I hope, at this point to be able to show you what and how...
----
What Apache? How Apache?
A very personal choice is what version of Apache you want to run. In my following examples I will assume you're going to use Apache 2.0, as it is now stableand much more performing than the "old" 1.3.
It's now several months that most of the sites hosted by VNU (my employer) are running 2.0 (apart from our old legacy "rolaren" server) and I never had in my personal experience a single problem.
Apache 2.0, though, is somehow more "difficult" to build and configure: the most difficult choice is the selection of the MPM (Multi-Process Module) to use. Read the manual to choose what suits you best, but in my case the "worker" MPM (multi-process, multi-threaded) is the one giving me the best performance/solidity ratio.
The "www.apache.org" website, on the other hand, uses the "prefork" MPM (multi-process, single threaded, exactly as Apache 1.3 did), but I feel that under certain operating system it is slightly slower than "worker". Your choice.
As a reference, I configure Apache 2.0 in the following way:
./configure \
--with-mpm=worker \
--enable-modules=all \
--enable-mods-shared=all \
--enable-proxy \
--enable-proxy-http \
--disable-ipv6
Basically, I use the workermodule, all modules are compiled as DSO modules (dynamically loaded, so that I can disable the ones I don't use), including the proxy/proxy-http module, and I don't care for IPv6 support.
----
Connecting Cocoon
As Stefano, I had several headaches trying to connect Apache and (name your Servlet container of choice). Mod_JK (JK2) doesn't work for me, mod_webapp works for me, but just for me because I'm the author, and was forced to sadly abandon its development, the only solution I see (and the one which works best for me currently) is mod_proxy.
Mod_proxy is a nice little module, especially in Apache 2.0 where its caching part is completely decoupled in another module (mod_cache), it's very small, lightweight, and does the job...
Plus, you have the advantage to choose whatever servlet container you have in the backend: Orion, WebSphere, Tomcat, Jetty, you name it, it supports HTTP :-) (well, apart from ServletExec, but that's another story, and if someone wants some hints, let me know).
Connecting Cocoon is simple: all you have to do is configure your servlet container to run on a high port (8080 for example) and make sure it runs as a non privileged user, make sure that it knows that is a proxied-HTTP server (Cocoon, Jetty, Resin, Orion, ... They all have this concept, check out the documentation), and configure Apache with those two lines:
ProxyPass / http://localhost:8080/ ProxyPassReverse / http://localhost:8080/
The first one tells Apache that any whatsoever request (from /-slash- onwards) gets "proxied" to localhost:8080, and the second one tells Apache to make sure that any LocationHTTP header coming back gets rewritten accordingly (just in case if your Servlet container doesn't let you set the "proxied" configuration).
That's IT. It runs, and it runs smoothly.
----
Trivially serving static files
Now, Apache is definitelyfaster than any Java based servlet container in serving files straight to HTTP clients. This is just because nowadays it uses a kernel-based function called sendfile, that makes its performances far greater than anything than Java can do.
Using mod_proxy and the set of ProxyPass configuration directive doesn't allow us to set a "pattern" to associate to resources to be served straight off the filesystem, it only allows us to define exclusion lists and processing lists.
In my example, then I will rewrite my configuration to make Apache serve everything beginning with /static/straight out of my web-application, without even touching the servlet container:
# Make sure that my document root points to the root of the web # application (where the WEB-INF is located, for instance). DocumentRoot /export/webapps/cocoon # We don't proxy any request beginning with the keyword "/static/". # So, for example, "/static/logo.gif" will be served directly by # Apache from the "/export/webapps/cocoon/static/logo.gif file" ProxyPass /static/ ! # Another one for "favicon.ico", so that explorer and mozilla are happy ProxyPass /favicon.ico ! # And now we send back to the servlet engine everyting else that does # not begin with "/static/" or "/favicon.ico" ProxyPass / http://localhost:8080/ ProxyPassReverse / http://localhost:8080/
Simple, the (exclamation mark) keyword in ProxyPass means "don't" :-)directive. Note that, though, the ErrorDocumentdirective requires a file (so it needs to be non proxied). Either you get down nasty with your mod_aliasconfigurations, or simply, use the second configuration and include it in your webapp as a static file. Anyway, what you have to specify in that case is simply:
# If mod_proxy cannot connect to the servlet container, we want # to display a nice static page saying the reason ErrorDocument 502 /static/unavailable.html
If (for example) you wanted to use Server-Side-Includes to render your page (it might be nice to display something like the host name, or the time when the request was received, you can do so by using SHTML files. This is what I use at home:
<html>
<head>
<title><!--#echo var="SERVER_NAME"-->: server off-line</title>
</head>
<body>
<h3><!--#echo var="SERVER_NAME"-->: server off-line</h3>
<p>
We are sorry, but the server is temporarily unavailable due to
maintenance. Our team is working to restore service as soon as
possible.<br />
In case of troubles, please feel free to contact our webmaster
sending an email to
<a href="mailto:<!--#echo var="SERVER_ADMIN"-->">
<<!--#echo var="SERVER_ADMIN"-->>
</a>.
</p>
<hr/>
<p>
<small>
<!--#echo var="SERVER_SOFTWARE"--> running on
<!--#echo var="SERVER_NAME"-->:<!--#echo var="SERVER_PORT"-->
at <!--#echo var="DATE_LOCAL"-->.
</small>
</p>
</body>
</html>
And to make it work properly this is how your httpd.confwill have to look like:
# Make sure that Server Side Includes are processed and sent
# to the client with mime-type as text/html
AddType text/html .shtml
AddOutputFilter Includes .shtml
# Make sure that our SHTMLs are processed in the static
# directory
<Directory "/export/webapps/cocoon">
Options IncludesNoExec
</Directory>
# If mod_proxy cannot connect to the servlet container, we want
# to display a nice static page saying the reason. This is a
# SHTML page (using the Server-Side-Includes filter)
ErrorDocument 502 /static/unavailable.shtml
----
Putting all together (step one)
Ok, now that we have seen how each piece gets together, let's try to put them all together, adding also that any request to /WEB-INF/should be forbidden straight away (there's no point in proxying them when we know that the servlet container will block them all)
# Make sure that my document root points to the root of the web
# application (where the WEB-INF is located, for instance).
DocumentRoot /export/webapps/cocoon
# Make sure that Server Side Includes are processed and sent
# to the client with mime-type as text/html
AddType text/html .shtml
AddOutputFilter Includes .shtml
# Make sure that our SHTMLs are processed in the static
# directory
<Directory "/export/webapps/cocoon">
Options +IncludesNoExec
</Directory>
# Block the stupid "WEB-INF" pseudo-url (god I wish web-applications
# were designed with some intelligence... Ok, my fault as well)
<Location /WEB-INF>
Order deny,allow
Deny from all
</Location>
# If mod_proxy cannot connect to the servlet container, we want
# to display a nice static page saying the reason. This is a
# SHTML page (using the Server-Side-Includes filter)
ErrorDocument 502 /static/unavailable.shtml
# We don't proxy any request beginning with the keyword "/static/".
# So, for example, "/static/logo.gif" will be served directly by
# Apache from the "/export/webapps/cocoon/static/logo.gif file"
ProxyPass /static/ !
# Another one for "favicon.ico", so that explorer and mozilla are happy
ProxyPass /favicon.ico !
# And now we send back to the servlet engine everyting else that does
# not begin with "/static/" or "/favicon.ico"
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/
Simple, easy, beautiful...
----
A more complex example: mod_rewrite
This is all nice and clean, but if we want to be really nasty, and starting to serve (for example) all our GIF and JPG files straight via Apache, we would need to use mod_rewrite.
I know, mod_rewriteis ugly, it uses PERL regular expressions (so, well, it's even slightly slower), but mod_proxyis way to crummy, it's either "in" or "out", and it takes over the whole world (you can't really do much else after you said you're going to forward a URL).
So, mod_rewrite, even if it's ugly, even if it's slower, isour solution. With a couple of rules, we can take the configuration written above to the extreme, and basically do WHATEVER we want with a URL beforeit even knows about a possible servlet container in the backend.
I suggest you to read carefullythe mod_rewrite documentation, but, as a start, I'm going to rewrite what's written above, using rewrite and its flags, from here on, you're on your own :-) :-)
# Make sure that my document root points to the root of the web
# application (where the WEB-INF is located, for instance).
DocumentRoot /export/webapps/cocoon
# Make sure that Server Side Includes are processed and sent
# to the client with mime-type as text/html
AddType text/html .shtml
AddOutputFilter Includes .shtml
# Make sure that our SHTMLs are processed in the static
# directory
<Directory "/export/webapps/cocoon">
Options +IncludesNoExec
</Directory>
# If mod_proxy cannot connect to the servlet container, we want
# to display a nice static page saying the reason. This is a
# SHTML page (using the Server-Side-Includes filter)
ErrorDocument 502 /static/unavailable.shtml
# The nastiness begins, let's fire up the "rewrite engine"
RewriteEngine On
# Everything that starts with "/static" or "/static/" is served straight
# through: no redirection, no proxying, no nothing, and the [L] flag
# implies that if this rule is matched, no other matching must be
# performed
RewriteRule "^/static/?(.*)" "$0" [L]
# Everything that starts with a NON-CASE-SENSITIVE match (the NC flag)
# of "/WEB-INF" or "/WEB-INF/" is forbidden (the F flag). And again,
# this is the last rule (the L flag), nothing will be processed by the
# rewrite engine if this rule is matched
RewriteRule "^/WEB-INF/?(.*)" "$0" [L,F,NC]
# Everything ending in ".gif", ".jpg" or ".jpeg" will be served again
# directly by Apache, no need to bother the servlet container. As above
# this is the last rule as specified by the [L] flag at the end
RewriteRule "^/(.*)\.gif$" "$0" [L]
RewriteRule "^/(.*)\.(jpg|jpeg)$" "$0" [L]
# Everything else not matched above needs to go to the servlet container
# via HTTP listening on port 8080. The [P] flag (which is required)
# implies that our requests will be handled by mod_proxy.
RewriteRule "^/(.*)" "http://localhost:8080/$1" [P]
# Make sure that if the servlet container specifies a "Location" HTTP
# header during redirection starting with "http://localhost:8080/", we
# can handle it and return to our client the effective (not real)
# location we want to redirect them to. This is _essential_.
ProxyPassReverse / http://localhost:8080/
As I mentioned before, ugly, but reallyeffective. In few lines we connect the HTTP-based servlet container running Cocoon to Apache, we make sure that if the servlet container falls over, we direct people to an appropriate holding page, we serve all that is under /static, all GIF and all JPEG files straight off without touching Cocoon and all the rest through our sitemap, and as a free bonus, everything that ends in ".shtml" (from disk or from the sitemap) will be passed through the Apache "Server-Side-Includes" filter (mod_include, which is ugly, but sometimes _really_ effective)...
----
Letting Apache to handle error pages
Whenever we want Apache to handle error messages in a consistent way (basically overwriting what Cocoon writes as a body in error pages), we can do that by simply adding a few lines to the configurations we used before:
# Make sure that Apache processes the headers coming back from the proxy # requests. This will enable also the evaluation of HTTP status codes. ProxyPassReverse / http://localhost:8000/ # Tell mod_mod proxy that it should not send back the body-content of # error pages, but be fascist and use its local error pages if the # remote HTTP stack is sending an HTTP 4xx or 5xx status code. ProxyErrorOverride On # For each individual error we want to handle, let's specify what file # we want to use. Note that all files must be available through a # locally accessible directory (as our /static/), and they can even be # SSI files (SHTML files). ErrorDocument 404 /static/notfound.shtml ErrorDocument 500 /static/error.shtml ErrorDocument 502 /static/unavailable.shtml
This is how it can be done, so that (for example, as suggested by Jeremy), one can configure Cocoon to dump full-stack-traces on the staging server, (or from an interface available only to the internal network), while displaying nicely formatted error messages to our client.
----
Preserving the Host header through a proxy
In some cases, it is quite important to preserve the Hostheader throughout the proxied request.
For example, to be able to deal with multiple virtual hosts on the backend servlet container, the proxied request MUST include the original Host name requested by our client. Apache allows us to pass this value through using the ProxyPreserveHost directive:
# Make sure that the virtual host name is passed through to the # backend servlet container for virtual host support. ProxyPreserveHost On
----
Putting it all together (step 2)
Linking together all the different pieces we've analyzed before, now, we can attempt to write up a do-it-all fragment of our httpd.conf file:
#######################################################################
# GLOBAL CONFIGURATIONS #
#######################################################################
# Make sure that my document root points to the root of the web
# application (where the WEB-INF is located, for instance).
DocumentRoot /export/webapps/cocoon
# Make sure that Server Side Includes are processed and sent
# to the client with mime-type as text/html
AddType text/html .shtml
AddOutputFilter Includes .shtml
# Make sure that our SHTMLs are processed in the static
# directory
<Directory "/export/webapps/cocoon">
Options +IncludesNoExec
</Directory>
#######################################################################
# ERROR PAGES CONFIGURATION #
#######################################################################
# If mod_proxy cannot connect to the servlet container, we want
# to display a nice static page saying the reason. This is a
# SHTML page (using the Server-Side-Includes filter)
ErrorDocument 502 /static/unavailable.shtml
# For each individual error we want to handle, let's specify what file
# we want to use. Note that all files must be available through a
# locally accessible directory (as our /static/), and they can even be
# SSI files (SHTML files).
ErrorDocument 404 /static/notfound.shtml
ErrorDocument 500 /static/error.shtml
#######################################################################
# MOD_PROXY CONFIGURATIONS #
#######################################################################
# Make sure that if the servlet container specifies a "Location" HTTP
# header during redirection starting with "http://localhost:8080/", we
# can handle it and return to our client the effective (not real)
# location we want to redirect them to. This is _essential_ to handle
# also the error returned by the backend servlet container.
ProxyPassReverse / http://localhost:8080/
# Make sure that the virtual host name is passed through to the
# backend servlet container for virtual host support.
ProxyPreserveHost On
# Tell mod_mod proxy that it should not send back the body-content of
# error pages, but be fascist and use its local error pages if the
# remote HTTP stack is sending an HTTP 4xx or 5xx status code.
ProxyErrorOverride On
#######################################################################
# MOD_REWRITE CONFIGURATIONS #
#######################################################################
# The nastiness begins, let's fire up the "rewrite engine"
RewriteEngine On
# Everything that starts with "/static" or "/static/" is served straight
# through: no redirection, no proxying, no nothing, and the [L] flag
# implies that if this rule is matched, no other matching must be
# performed
RewriteRule "^/static/?(.*)" "$0" [L]
# Everything that starts with a NON-CASE-SENSITIVE match (the NC flag)
# of "/WEB-INF" or "/WEB-INF/" is forbidden (the F flag). And again,
# this is the last rule (the L flag), nothing will be processed by the
# rewrite engine if this rule is matched
RewriteRule "^/WEB-INF/?(.*)" "$0" [L,F,NC]
# Everything ending in ".gif", ".jpg" or ".jpeg" will be served again
# directly by Apache, no need to bother the servlet container. As above
# this is the last rule as specified by the [L] flag at the end
RewriteRule "^/(.*)\.gif$" "$0" [L]
RewriteRule "^/(.*)\.(jpg|jpeg)$" "$0" [L]
# Everything else not matched above needs to go to the servlet container
# via HTTP listening on port 8080. The [P] flag (which is required)
# implies that our requests will be handled by mod_proxy.
RewriteRule "^/(.*)" "http://localhost:8080/$1" [P]
And that's all... You can roughly copy and paste this example in a <VirtualHost>section of your httpd.conf(obviously after having applied the appropriate modification), and go...
----
Conclusions
I hope to have cleared some of the doubts on Apache, and why I love it so much... It is a hub, a hub embracing your website and making it work better, faster, more reliably and exactly fine-tuned precisely as you (or your boss) like it.
And you can trust Apache, I believe that our spirit, the spirit of the entire Cocoon community is built on top on the original HTTPd vision of let's make things work so nicely that the world won't have to look for another solution...
HTTPd does it in its little piece of being an HTTP hub, Jetty does it in its little piece of being a servlet container, Cocoon does it in its little piece of being the best "web-application" framework available on the planet right now. Together, those three little pieces willconquer the world.
Have fun...\\Pier
----
Notes from Cal - when mounting applications using reverse proxying it appears that you need to keep the url intact for state and session information to be preserved. \\ e.g. "ProxyPass /cocoon/ http://someserver.com:8080/cocoon/" works and the state examples that come with cocoon work properly \\ but "ProxyPass /proxy/cocoon/ http://someserver.com:8080/cocoon/" seems to break the state examples by inserting /proxy/ into the url. This is the same for a php application i have on another server sugesting that this is a general session thing not just specific to cocoon
----
notes from RBD on proxying with different names \\ i think that the cause of the above is that servlet containers set the path property of the session cookie. the means that most browsers will not send back the cookie. \\ for the brave using open source containers, you could try rolling your own version of your container which supports setting different paths.
----
More notes from Cal - in order to prevent people from using your proxy to cover their tracks deny access to any request that doesn't start with a / \\
<LocationMatch "^[^/]"> Deny from all </LocationMatch>
People scan for open proxies that will allow proxying of "http://" requests so they set their browser to use it.
----
Notes from TonyCollen:
I've realized that mod_proxy can also be used in a way that will allow you to proxy through requests to virtualhosts, which I will write up very soon now.
AuthWithTomcat
Authentication and Authorisation by the servlet container
This document describes how to let the servlet container handle webapp security. There are two parts to security that we consider here:
- authentication: this is finding out who the user really is. Usually done by asking a username and password but could also be using e.g. a PKI infrastructure.
- authorisation: once you know who the user is, decide whether he or she is allowed to access certain resources
You can handle this manually by creating a login form and then using Actions in the sitemap to check if the user is logged in. Better yet, there's a framework to do this in Cocoon described here.
Doing the authorisation in the sitemap gives more flexibility regarding what parts of the pipeline should be protected compared to using the security features of the servlet container. However, in many cases what the servlet container offers may be more then enough and is easier and quicker to set up (in my opinion, of course).
In this document we will describe how to set up servlet security for Tomcat 4.
First of all, you need to configure a realm in Tomcat's server.xml. How this can be done is described here.
Basically this involves three things:
- make a user- and roles database
- place the JDBC driver in server/lib
- add a Realm-element in the conf/server.xml (do not forget to disable the default one!)
But all this is described in the document referenced before.
The next step is to edit Cocoon's web.xml (usually found in $TOMCAT_HOME/webapps/cocoon/WEB-INF/web.xml), and add something like shown below near the bottom of the file (before the closing </webapp> tag -- consult the web.xml DTD if unsure).
<security-constraint>
<web-resource-collection>
<web-resource-name>MySecretResource</web-resource-name>
<url-pattern>/topsecret/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>My protected area</realm-name>
<form-login-config>
<form-login-page>/login</form-login-page>
<form-error-page>/loginfailed</form-error-page>
</form-login-config>
</login-config>
The url-pattern element describes all the resources to be protected. This should start with a slash and end with '/*'. The path is relative to the context path on which the webapp is mounted. You can notuse more complex expressions like in Cocoon's matchers. You can however use multiple url-pattern elements. It could also specify a single resource, by not ending the path on '/*'.
The login-config element describes how the login should happen. In the example above we have used form-based login, which requires us to create login pages. An alternative is basic authentication, in which case the browser will show a popup window asking for your credentials.
To use basic authentication, replace the login-config element from the above example with this:
<login-config> <auth-method>BASIC</auth-method> <realm-name>My protected area</realm-name> </login-config>
Note also that form-based login requires (creates) a session, and will thus always be a little heavier on the server. It will also require cookies to be enabled in users' browsers to keep track of the session.
In case you have chosen to use basic authentication, your work ends here. Restart tomcat and try to access a protected URL. It should ask your user name and password.
In case you have have chosen to use form-based login, you need to create the login pages. These will be generated by Cocoon pipelines. To keep it simple, here we'll use static HTML files. Create two files in $TOMCAT_HOME/webapps/cocoon as follows:
login.html
<html>
<head>
<title>Login</title>
</head>
<body>
<form method="POST" action="j_security_check">
<table border="0">
<tr><td>Username:</td><td><input type="text" name="j_username"></td></tr>
<tr><td>Password:</td><td><input type="password" name="j_password"></td></tr>
<tr><td></td><td><input type="submit"/></td></tr>
</table>
</form>
</body>
</html>
loginfailed.html
<html>
<head>
<title>Login</title>
</head>
<body>
<b>Login failed!</b>
<form method="POST" action="j_security_check">
<table border="0">
<tr><td>Username:</td><td><input type="text" name="j_username"></td></tr>
<tr><td>Password:</td><td><input type="password" name="j_password"></td></tr>
<tr><td></td><td><input type="submit"/></td></tr>
</table>
</form>
</body>
</html>
The special resource "j_security_check" used in the action attribute of the form is a special resource recognized by Tomcat. It is not needed to create a matcher for that one in the sitemap.
Now, we need to add matchers to the sitemap for the login pages. It is recommended to place the login pages themselves outside the protected area, because if the page references images which are also in the protected area, you will get into troubles. This is because (1) the images themselves cannot be accessed if they are in the protected area and (2) after login, Tomcat will redirect you to the last requested protected URL which will in that case be the image.
Here is an example sitemap snippet:
<map:match pattern="login"> <map:read src="login.html" mime-type="text/html"/> </map:match> <map:match pattern="loginfailed"> <map:read src="loginfailed.html" mime-type="text/html"/> </map:match>
The patterns of the matchers should of course correspond to what you configured in the web.xml in the elements form-login-page and form-error-page.
Now you are ready to try it out. Restart Tomcat and try accessing a protected resource, and you will get the login form.
A last problem now remains: how to log out?
In case of basic authentication, the user needs to close its browser window.
In case of form-based authentication, you can invalidate the session to log out. This can be done with a pipeline like this.
<map:match pattern="logout">
<map:act type="session-invalidate"/>
<map:act type="request">
<map:parameter name="parameters" value="true"/>
<map:redirect-to uri="{goto}"/>
</map:act>
</map:match>
In this case, you would place a link to "/logout?goto=somewhere" which would logout the user and redirect him or her to page "somewhere".
Note that the "session-invalidate" action used above ships with Cocoon (2.03) but is not declared by default in the sitemap. Therefore, add this in the map:actions section of the sitemap:
<map:action
name="session-invalidate"
src="org.apache.cocoon.acting.SessionInvalidatorAction"/>
If you get the error "Invalid direct reference to form login page"
If you are getting this error, it usually means that either:
- you don't have cookies enabled
- you surfed directly to the login page. You should instead request a protected resource, which will then redirect you to the login page.
BeginnerInstallTomcatUnix
installing tomcat in 15 minutes (cookbook approach for unix/linux)
- TARGET-AUDIENCE: beginners\\ - COCOON-RELEASES: n.a\\ - DOCUMENT-STATUS: draft*reviewed released\\ ---- This document applies to :
- tomcat-4.1.18 and newer under linux and unix
What you will get from this page
You will learn, how to retrieve and setup tomcat, the official reference ServletContainer. This approach has been tested with tomcat 4.1.18 and newer releases.
Your basic skills
I assume, you
- have minimal knowledge about webapplications and application servers in general.
- know how to unzip or untar a compressed package
Technical prerequisites
In order to install tomcat you need
- an operating Java-1.2, or a newer Java Virtual Machine
- some basic knowledge in "sh" (unix shell)
Links to other information sources
For general infos about installation of tomcat refer to the official Tomcat site. See\\ http://jakarta.apache.org/tomcat/tomcat-4.1-doc/RUNNING.txt\\ for download install build and so on.
Step I: Get the distrib
Retrieve the current distribution from\\ http://apache.serveftp.org/apache-site/dist/jakarta/tomcat-4/binaries/
On the distribution directory you will find several tomcat packages suitable for different operating systems anddifferent java-versions. For linux or unix you will download the tar.gz distribution.
caveat: Make shure, which java version you have installed. If you are running the Java-1.4 runtime or JDK, please retrieve one of the tomcat distributions, which are clearly labeled with jdk14. If you are running a lower version of java, you mustdownload one of the other distributions. You can check which java version you are running by issuing the following command:
java -version
Step II: Unpack it
unpack the distrib to any convenient place.
Unfortunately several conventions apply depending on which operating system you are working. And it depends, for which user group you are planning to install the Servlet Engine. If you install tomcat for your personal usage only, choose any convenient place under your home folder. If you are setting up a server for many users, it would be common place to choose /opt/tomcat as root of your installation. I personally choose /opt/tomcat, place the tar file into this directory, then i untar it at place. I end up with a subfolder named jakarta-tomcat-4.1.18. The following set of commands will do the job:
mkdir /opt/tomcat cd /opt/tomcat # You need to retrieve the distrib now to /opt/tomcat tar xzf tomcat-4.1.18.tar.gz
caveat: some unix tars do NOT support the uncompression flag 'z' in these case you either need to gunzip first. The command sequence would be:
gunzip tomcat-4.1.18.tar.gz tar xf tomcat-4.1.18.tar
Step III: Prepare for startup
Once you have installed tomcat, you need to prepare it for startup. I recommend to try first starting the distribution as is. You only need to modify something, if the following ports are already occupied by some other program:
8005|Tomcat Shutdown port for maintenance 8080|Tomcat Standard http port 8009|Tomcat standard port for connection to a webserver
Please note, that your Tomcat server comes with an included HTTP-server. So you don't need a webserver like the apache-server to run your servlets. You simply start tomcat, then you are ready to access it from your browser.
modify the standard port settings
As pointed out above, you need to do this only, if you are sure, that the Tomcat standard ports are already occupied. If you are unsure, just start Tomcat and check for errors telling about "Adress already bound"-messages. If this occurs, come here and do the following:
- Enter the subfolder named "conf"
- edit the file named server.xml
- Search for the shutdown port definition
<Server port="8005" shutdown="SHUTDOWN" debug="0">
You may change the port to any convenient number above 1024 and below 32768.
- Search for the http port definition
<!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 9011 --> <Connector className="org.apache.coyote.tomcat4.CoyoteConnector" port="8080" minProcessors="5" maxProcessors="75"
You may change the port to any convenient number above 1024 and below 32768.
- Search for the Webserver connector
<!-- Define a Coyote/JK2 AJP 1.3 Connector on port 8009 --> <Connector className="org.apache.coyote.tomcat4.CoyoteConnector" port="8009" minProcessors="5" maxProcessors="75" enableLookups="true" redirectPort="8443" acceptCount="10" debug="0" connectionTimeout="0" useURIValidationHack="false" protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler"/> <!-- Define an AJP 1.3 Connector on port 8009 --> <!-- <Connector className="org.apache.ajp.tomcat4.Ajp13Connector" port="8009" minProcessors="5" maxProcessors="75" acceptCount="10" debug="0"/> -->
Depending on your system, either the Coyote/JK2 AJP Connector, the AJP 1.3 Connector is active. The inactive connector is commented out with XML comments of following syntax:
<!-- anything except 2 consecutive '-' signs -->
You may inactivate both connectors for the moment, but need to reactivate one as soon as you want to connect your Webserver with tomcat.
Start your container for the first time
Enter the subfolder named "bin" of your distribution. You will find a set of usefull scripts:
catalina.sh best for manual startup from commandline startup.sh best for script based startup shutdown.sh best for script based shutdown
For the beginning you may use catalina.sh :
cd /opt/tomcat/jakarta-tomcat-4.1.16/bin ./catalina.sh
You have started tomcat now in foreground. The shell is blocked and tomcat runs. You can stop tomcat by pressing CTRL-C at any time.
An alternative way is to use ./startup.shinstead of ./catalina.shThis would start tomcat in the background sending the screen output into a logfile instead. To stop tomcat in background mode, use the script ./shutdown.sh
Verify operation
Once you have started tomcat, you can use your Browser to access the Welcome page. Assuming you have not modified the $tomcat_root/bin/server.xml as described above, you may now access tomcat by typing in your browser:
http://localhost:8080
You might have to replace localhostby the name of your server only, if your browser runs on a different machine. If you see the tomcat welcome message, chances are very high, that your installation was correct. If you don't see anything see below.
Where to go next
Now you have managed to install the Tomcat Servlet Container. You have verified, that your container operates in your environment. Now it's time to get cocoon into play. I propose following documents:
setup cocoon in 15 minutes: We will show you, how you can setup cocoon within your new Servlet Container.
First aid if something goes wrong.
There is always the chance, that something goes wrong during setup. The most critical part of the installation is the modification of the file $tomcat_root/conf/server.xml
Here are some tips, how to proceed if Tomcat won't show up.
Look at the startup logs
You will find the logs under $tomcat_root/logs This is a typical list of logfiles, you may find:
catalina.out\\ localhost_log.2003-01-15.txt\\ catalina_log.2003-01-14.txt\\ localhost_admin_log.2003-01-14.txt\\ localhost_examples_log.2003-01-14.txt\\
- If you encounter any problem with tomcat it is best to first look into
catalina.out.
- If you can't find a clue to your problem there, look next at
localhost_log.<date>.txt. You should get a clue by now.
- If you still can't access your server with your browser, recheck the first lines of
catalina.outand look for following pattern:
[INFO] Http11Protocol - -Initializing Coyote HTTP/1.1 on port 8080 Starting service Tomcat-Standalone Apache Tomcat/4.1.18
and verify that your portsettings are corect.
- And don't forget to enter the correct URL to your browser:
http://loalhost:8080
----
page metadata
- AUTHOR: Hussayn Dabbous\\ - AUTHOR-CONTACT: hussayn.dabbous@saxess.com\\ - REVIEWED-BY: \\ - REVIEWER-CONTACT:\\
BeginnerInstallTomcatWindows
installing tomcat in 15 minutes (cookbook approach for windows)
- TARGET-AUDIENCE: beginners\\ - COCOON-RELEASES: n.a\\ - DOCUMENT-STATUS: draft*reviewed released\\ ---- This document applies to :
- tomcat-4.1.18 and newer on Windows based Systems
What you will get from this page
You will learn, how to retrieve and setup tomcat, the official reference ServletContainer. This approach has been tested with tomcat 4.1.18 and newer releases.
Your basic skills
I assume, you
- have minimal knowledge about webapplications and application servers in general.
- know how to install a package
- basic knowledge of DOS-Box execution
Technical prerequisites
In order to install tomcat you need
- an operating Java-1.2, or a newer Java Virtual Machine
Links to other information sources
For general infos about installation of tomcat refer to the official Tomcat site. See\\ http://jakarta.apache.org/tomcat/tomcat-4.1-doc/RUNNING.txt\\ for download install build and so on.
Step I: Get the distrib
Retrieve the current distribution from\\ http://apache.serveftp.org/apache-site/dist/jakarta/tomcat-4/binaries/
On the distribution directory you will find several tomcat packages suitable for different operating systems. You would retrieve the .exe version, which comes along with a nice and easy to use install shield. If you want to use winzip, you also can download the zip distribution.
caveat: Make shure, which java version you have installed. If you are running the Java-1.4 runtime or JDK, please retrieve one of the tomcat distributions, which are clearly labeled with jdk14. If you are running a lower version of java, you mustdownload one of the other distributions. You can check from a DOS-Box which java version you are running by issuing the following command:
java -version
Step II: Unpack it (if you use the zip-distrib)
unpack the zip-distribution unpack the distrib to any convenient place. You should use the convention to unpack under the folder
c:\programs\tomcat
install the installshield based exe-version
you may simply call this exe file now. An installer will open up. You may complete the installing process with the given defaults. At the end your tomcat server is ready to operate and you can start it from the start menu
Start -> Programs -> Apache Tomcat 4.* -> Start Tomcat
You are ready with the installation. You may continue at the paragraph "startup Tomcat for the forst time"
Step III: Prepare for startup (only zip-distrib)
Once you have installed tomcat, you need to prepare it for startup. I recommend to try first starting the distribution as is. You only need to modify something, if the following ports are already occupied by some other program:
8005|Shutdown port for maintenance 8080|Standard http port 8009|standard port for connection to a webserver
Please note, that your tomcat server comes with an included http-server. So you don't need a webserver like the apache-server to run your servlets. You simply start tomcat, then you are ready to access it from your browser.
modify the standard port settings
As pointed out above, you need to do this only, if you are shure, that the Tomcat standard ports are already occupied. If you are unshure, just start Tomcat and check for errors telling about "Adress already bound"-messages. If this occurs, come here and do the following:
- Enter the subfolder named "conf"
- edit the file named server.xml
- Search for the shutdown port definition
<Server port="8005" shutdown="SHUTDOWN" debug="0">
You may change the port to any convenient number above 1024 and below 32768.
- Search for the http port definition
<!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 9011 --> <Connector className="org.apache.coyote.tomcat4.CoyoteConnector" port="8080" minProcessors="5" maxProcessors="75"
You may change the port to any convenient number above 1024 and below 32768.
- Search for the Webserver connector
<!-- Define a Coyote/JK2 AJP 1.3 Connector on port 8009 --> <Connector className="org.apache.coyote.tomcat4.CoyoteConnector" port="8009" minProcessors="5" maxProcessors="75" enableLookups="true" redirectPort="8443" acceptCount="10" debug="0" connectionTimeout="0" useURIValidationHack="false" protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler"/> <!-- Define an AJP 1.3 Connector on port 8009 --> <!-- <Connector className="org.apache.ajp.tomcat4.Ajp13Connector" port="8009" minProcessors="5" maxProcessors="75" acceptCount="10" debug="0"/> -->
Depending on your system, either the Coyote/JK2 AJP Connector, the AJP 1.3 Connector is active. The inactive connector is commented out with XML comments of following syntax:
<!-- anything except 2 consecutive '-' signs -->
You may inactivate both connectors for the moment, but need to reactivate one as soon as you want to connect your Webserver with tomcat.
Start your container for the first time
installed exe-version:\\
from the start menu
Start -> Programs -> Apache Tomcat 4.* -> Start Tomcat
zip-distribution:\\
Enter the subfolder named "c:\programs\tomcat\bin" of your distribution. You will find a set of usefull batch files:
catalina.bat best for manual startup from commandline startup.bat best for script based startup shutdown.bat best for script based shutdown
You may use catalina.bat for the beginning:
cd C:\ptograms\tomcat\jakarta-tomcat-4.1.16\bin ./catalina.bat
You have started tomcat now in foreground. The shell is blocked and tomcat runs. You can stop tomcat by pressing CTRL-C at any time.
An alternative way is to use ./startup.bat instead of ./catalina.bat This would start tomcat in the background sending the screen output into a logfile instead. To stop tomcat in background mode, use the batch file shutdown.bat
Verify operation
Once you have started tomcat, you can use your Browser to access the Welcome page. Assuming you have not modified the server.xml as described above, you may now access Tomcat by typing in your browser:
http://localhost:8080
You might have to replace localhostby the name of your server if your browser runs on a different machine. If you see the Tomcat welcome message, chances are very high, that your installation was correct. If you don't see anything see below.
Where to go next
Now you have managed to install the Tomcat Servlet Container. You have verified, that your container operates in your environment. Now it's time to get cocoon into play. I propose following documents:
setup cocoon in 15 minutes: We will show you, how you can setup cocoon within your new Servlet Container.\\
First aid if something goes wrong.
There is always the chance, that something goes wrong during setup. The most critical part of the installation is the modification of the file $tomcat_root\conf\server.xml
Look at the startup logs
You will find the logs under $tomcat_root/logs This is a typical list of logfiles, you may find:
catalina.out\\ localhost_log.2003-01-15.txt\\ catalina_log.2003-01-14.txt\\ localhost_admin_log.2003-01-14.txt\\ localhost_examples_log.2003-01-14.txt\\
- If you encounter any problem with tomcat it is best to first look into
catalina.out.
- If you can't find a clue to your problem there, look next at
localhost_log.<date>.txt. You should get a clue by now.
- If you still can't access your server with your browser, recheck the first lines of
catalina.outand look for following pattern:
[INFO] Http11Protocol - -Initializing Coyote HTTP/1.1 on port 8080 Starting service Tomcat-Standalonel Apache Tomcat/4.1.18
and verify that your portsettings are corect.
- And don't forget to enter the correct URL to your browser:
http://loalhost:8080
----
page metadata
- AUTHOR: Hussayn Dabbous\\ - AUTHOR-CONTACT: hussayn.dabbous@saxess.com\\ - REVIEWED-BY: \\ - REVIEWER-CONTACT:\\
BeginnerSimpleWebappOrganisation
Organising your development (cookbook)
- TARGET-AUDIENCE: beginner*advanced expert\\ - COCOON-RELEASES: 2.0.3, 2.0.4\\ - DOCUMENT-STATUS: draft*reviewed released\\ ----
What you will get from this page
I will show you how you can setup a cleanly separated development environment with minimal modifications of the existing Cocoon infrastructure. You will end up with a hierarchy of folders all completely under your control with only one modification in the Cocoon standard distribution.
Your basic skills
- You have basic knowledge about XML
- You know how to access a command line shell to your OS
Technical prerequisites
- You need a cleanly installed version of Cocoon.
If you don't have access to Cocoon right now, you can just install it on your local computer. Please refer to BeginnerInstallation for further information.
Links to other information sources
...
The problem: Organisational complexity
Once you have installed Cocoon you will want to start doing something valuable with the system. The very first encounter with Cocoon will take place on the Cocoon sitemap, which happens to be a file right in the root directory (the $cocoon_root) of the Cocoon webapplication: sitemap.xmap.
As you will read elsewhere, the sitemap.xmapis the heart of Cocoon and, in order to get Cocoon doing something for you, you will first have to modify this file. Then you open the file and first get overwhelmed with a rather complex XML-structure! There is a clear need to keep this as untouched as possible. Not only do you want to keep the Cocoon infrastructure intact and operating, but you also have the feeling that you want to keep your work cleanly separated from the Cocoon distribution.
The solution: Separating your work by use of subsitemaps
Because Cocoon is designed for high scalability, there is a concept of "divide and conquer" just built into the sitemap concept. The concept of submountingis well described in the Cocoon documentation. See chapter "the sitemap"
We will use subsitemaps in order to get our work separated out into a hierarchy of subfolders. And here is how it's done:
Part I: Use the existing possibilities
I will point you to a detail right in the middle of the originally distributed $cocoon_root/sitemap.xmap(near line 860): There you find following code snippet (i took off the comments here):
<map:pipeline>
<map:match pattern="mount/*/**">
<map:mount check-reload="yes" src="mount/{1}/" uri-prefix="mount/{1}"/>
</map:match>
</map:pipeline>
This little piece of the sitemap allows you to separate your work cleanly from the rest of cocoon. What does it do ? Look at the pattern="mount/*/**"definition in the secnd line. This match pattern is interpreted by cocoon as follows:
a - Any URL, that points to the Cocoon webapp\\ b - followed by a 'mount/'\\ c - followed by an arbitrary foldername (the first "*" in the pattern)\\ d - followed by a '/' (the "/" in the pattern)\\ e - followed by a path of arbitrary depth ("**" in the pattern).
When the interpreter hits the wildcard character '*' or the sequence '**' in the pattern, it will automatically resolve the actual value into local sitemap-parameters, where the first wildcard-character is resolved into the parameter {1}, then secnd wildchard-character is resolved into the parameter {2}, ...
Following rules apply:
- a single '*' is resolved into any character sequence inbetween two '/' characters.
- a double '**' is resolved into any character sequence delimited by a '/*' on the right side or a */ at the left side.
These abstract rules can best be understood by looking at an example. In the following we apply the URL (given in the first line) to the match-pattern (in the secnd line):
http://localhost:8080/cocoon/ mount/ work/index.xml <-- URL
~~~~~~~~~~~a~~~~~~~~~~~~~~~~~ ~~b~~~ ~~c~d~e~~~~~~~
mount/ * / ** <-- pattern
{1} {2} <-- sitemap-parameters
Here Cocoon associates the string "work" to the parameter {1} and the string "index.xml" to the parameter {2}. These parameters can be freely used within the pipeline snippet as you can see above. In this case we only make use of {1} within in the <map:mount> tag.
Cocoon tries to open a subsitemap within the folder indicated by the "src" attribute of the <map:mount> tag. In this example that would be $cocoon_root/mount/{1}/which resolves to $cocoon_root/mount/work/as you can conclude from the explaination above.
If you take a look at $cocoon_root you will already find the subfolder named "mount". All you have to do right now is create a new folder of the same name as {1} ("work" in the example above) right within the mount folder and start your work therein. And now we are ready for connecting your "work" folder to cocoon:
Part II: Connecting your "work" folder to Cocoon
Each time Cocoon is accessed with a URL of the form http://localhost:8080/cocoon/ mount/{yourFolder}/{path of arbitrary depth}, it will look in the $cocoon_root/mount/{yourFolder} folder for a sitemap.xmapfile. Within $cocoon_root/mount/{yourFolder}/sitemap.xmapyou will now define your project specific components, e.g. your pipelines. At the beginning of your development, this file may be very short.
Assume you want to serve an XML-content that is serialized into an HTML-output by use of an XSL-transformation. This, I think, is the simplest "use case" for Cocoon. I assume, the XSLT transformation is kept in the file "mytransform.xsl" and all files you want to serve have the ending ".xml". The setup is then straightforward:
- Enter your work folder
- Create a new sitemap.xmap within your "work" folder and place following content into the newly created file:
<?xml version="1.0"?>
<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
<map:components>
<map:generators default="file"/>
<map:transformers default="xslt"/>
<map:readers default="resource"/>
<map:serializers default="html"/>
<map:matchers default="wildcard"/>
</map:components>
<map:pipelines>
<map:pipeline>
<!-- xml files -->
<map:match pattern="*.html">
<map:generate src="{1}.xml"/>
<map:transform src="mytransform.xsl"/>
<map:serialize type="html"/>
</map:match>
</map:pipeline>
</map:pipelines>
</map:sitemap>
What did we achieve?
First we told Cocoon to start a xslt transformation whenever we try to retrieve an html file from the "work" folder. e.g., the following URL:
http://localhost:8080/cocoon/mount/work/index.html
gets Cocoon to look for a file named "index.xml" within your work folder. Then the file "index.xml" is transformed by use of the XSLT transformation stored in "mytransform.xsl". Finally the result is serialised by use of the HTML-serialiser. The final result is sent to the browser as standard html file.
Note: There is no file named index.htmlanywhere in your folder. This is only the pattern, that triggers the processing of the file index.xml and eventually sending back HTML-content to the browser! Of course you may want to name your xsl transform differently, or you may choose not to use the pattern "*.html" to trigger this pipeline. From here it's up to you, how you proceed.
Please remember only one crucial point: the match patterns are completely decoupled from the true names of files in your folder. e.g. you may choose the pattern "*.xml" instead of "*.html" and keep the rest of the pipeline as is. Then Cocoon would send back your html-result, if it encounters the URL
http://localhost:8080/cocoon/mount/work/index.xml
(see here: Although you ask for "index.xml", you get back a html result!)
Advanced issue: separating your work folder from Cocoon
Now you have managed to separate your work from the Cocoon distribution. But you still work right within the cocoon webapp folder. This may be not desirable for several reasons. You will find more detailed information in BeginnerAdvancedWebappOrganisation.
Appendix
An xml you can put on your work directory (index.xml):
<?xml version="1.0"?>
<page>
<title>Basic XML/XSL Transformation Example </title>
<greeting>Hello World</greeting>
</page>
An XSL stylesheet (mytransform.xsl) for above:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="page">
<html>
<head>
<title><xsl:value-of select="title"/></title>
</head>
<body>
<h1><xsl:value-of select="title"/></h1>
<p><xsl:value-of select="greeting"/></p>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
(Note: Make sure that the first processing instruction (<?xml version="1.0"?>) in the XML document has no spaces before it, this will result in an internal server error)
----
page metadata
- AUTHOR: Hussayn Dabbous\\ - AUTHOR-CONTACT: Hussayn.dabbous@saxess.de\\ - REVIEWED-BY: DerekH(minor editing)\\ Maskkkk (Added note at bottom)\\ - REVIEWER-CONTACT:\\ ----
BlocksDefinition
We're still experimenting with how to best use the wiki for collaborative design, please be careful or ask on cocoon-dev before making changes to this page.
Reader's comments are welcome at the end of this page, possibly using footnote references to point to the original text.
This definition was authored by Stefano Mazzocchi and posted on cocoon-dev.
Part 1: introduction
A step back: what are the problems we are trying to solve
Cocoon is currently a framework implemented as an application.
A 'framework' is supposed to give services to entities included in it, while an application is supposed to be executed by a containing framework.
While the above might sound weird at first, this is a very common situation: an operating system is a framework implemented as an application run at boot time. At the same time, an application server is a framework implemented as an application. But even a browser is a framework implemented as an application.
So, there is no inherently bad design in this concept, *but* the framework must be implemented in such a way that it's inherently *easy* to deploy/install/plug-in/connect/attach/inject/link an internal application that must be executed by the framework.
Cocoon lacks this.
Let me give you an example: I would like to be able to package my stuff that I wrote to be run *by* cocoon and deploy it on Cocoon, maybe even at runtime.
The parallel is easily made: servlets and WARs archives. The Servlet API introduced in a later release the concept of a WAR (Web ARchive) package that includes all the resources needed for the servlet/jsp-based web application to run, including libraries, resources, files and everything.
So, the parallel I want to draw is simple:
WAR (Web ARchive) -> tomcat (or other servlet container) COB (COcoon Block) -> cocoon
so, a WAR package is for tomcat what a COB will be for Cocoon.
In very short terms: a way for you to deploy your stuff on Cocoon without hassle (including special libraries, resources and what not).
Are we really cloning the servlet API?
Many people from the pure J2EE world (even Apache people) believe that Cocoon is just an attempt to rewrite the servlet API for XML. In a sense it's true: the servlet API wasn't designed for pipelines and the deployment descriptor wasn't designed for serious URI space mapping.
So, while the Servlet API introduces components (servlets and filters) that are based on streams of bytes/chars, Cocoon introduces components designed to be part of a pipeline (since 1997 I thought about a way to allow servlet chaining to be feasible, that is probably what triggered the idea of pipeline components for Cocoon).
Anyway, looking at this parallel, Cocoon really lacks a way to make its applications deployed easily within a 'naked' container that includes only the basic and default machinery.
- o -
I'm pretty sure that if I stopped here and went on describing the schema of the COB descriptor file and so on, people would love it, thank me, run to their boss to tell them and blah blah.
Sure, we could stop here, we could clone the WAR concept inside Cocoon, allow you to deploy your stuff and you won't be missing anything.
But there are two things that the servlet API architects didn't consider (not even myself at that time since I was part of that group): polymorphism and inheritance.
Applying Avalon COP philosophy over again
If you ever worked with Avalon, you know the feeling: at first it doesn't make any sense at all. It's a mess of stupid and very abstract interfaces... but after a while, a pattern emerges and it sticks.
Some might think that Avalon (probably Cocoon itself) includes infecting 'memes' and I agree. (Look up the name on google if you don't know what I'm talking about)
Once you start using COP (component oriented programming), it's very hard to go back (so much so that many abuse it and over-componentize their systems... even Cocoon itself suffers from this problem on some parts).
COP is based on IoC (Inversion of Control) and SoC (Separation of Concerns) (for those who still don't know about them!) and while the servlet API makes extensive use of the IoC metapattern, SoC doesn't play a clear and defined role (they tried to patch it with RequestDispatcher, which is the biggest hack I ever seen, I even voted against it but I was overruled).
Anyway, if the servlet API, internally, show use of IoC and SoC, externally, from the WAR point of view, there is *absolutely* no notion of it: a WAR is a package that includes a single and isolated application.
Period. That's it. There are many mechanism that enforce the clear separation between different WARs. So, they implement monolithic web applications and this is *by design*.
Improvements
Improvement #1: component-oriented deployment
Let me give you a possible use-case scenario.
Let us suppose that we implement WAR-like package deployment on top of Cocoon and that your application requires both PDF serialization and SVG->PNG rasterization.
Then, you implement another cocoon web application and you still require PDF generation.
Unfortunatley, since WAR-like installation isolates the packages and their classloaders, you have to install the PDF serialization libraries twice.
Thus the idea of blocks as units of deployable service. Here is a picture:
- case 1: WAR-like deployment
+----------------+ +--------------------+
| +-------+| |+-----+ +-------+|
| | FOP || || FOP | | Batik ||
| +-------+| |+-----+ +-------+|
| | | |
| webapp1 | | webapp2 |
+----------------+ +--------------------+
- case 2: block-like deployment
+-----+ +-------+
| FOP | | Batik |
+-----+ +-------+
| \ |
| \ |
+---------+ +---------+
| webapp1 | | webapp2 |
+---------+ +---------+
The second case allows:
- optimization of resources (libraries are not deployed more than needed)
- separate distributions (different packages can be prepared and
maintained by different groups independently, as long as the service contracts remain the same)
Improvement #2: polymorphic behavior
The above solution already improves on the WAR model, but we can do better than this. Another use-case scenario:
In the previous scenario, your web application required PDF serialization and, in fact, it mixes concerns if it depends *explicitly* on FOP since, later on, you might want to use another library/service that implements the same (for example iText or RenderX).
So, instead of depending on a particular *implementation* of a service behavior, if we make blocks depending on *behaviors* directly (considered as service contracts) we can implement polymorphic behavior of blocks.
Again, let's visualize it:
- case 1: dependency on implementation
+-----+ +-------+
| FOP | | Batik |
+-----+ +-------+
| \ |
| \ |
+---------+ +---------+
| webapp1 | | webapp2 |
+---------+ +---------+
- case 2: dependency on behavior
+-----------+ +-----------+
| +-----+ | | +-------+ |
| | FOP | | | | Batik | |
| +-----+ | | +-------+ |
| FO->PDF | | SVG->PNG |
+-----------+ +-----------+
| \ |
| \ |
+---------+ +---------+
| webapp1 | | webapp2 |
+---------+ +---------+
Here, the webapp1 requires "fo-pdf" serialization services but it does not care (nor should!) which implementation of this service is actually located into the system.
It is, in fact, the installer's concern to indicate whatblock that implementsthat behavior should be used in that system at that time.
Note that this allows several very intersting things:
- versioning: it is possible install several different versions of the same block and try them out (even at runtime) and roll-back if the version creates incompatibilities without having to change anything in the blocks, but only using the block manager (which is the part of cocoon responsible for deployment and configuration of blocks in the system).
- polymorphism: I can have different implementations of the same behavior and I can switch them simply by acting on the block manager, without having to touch a single configuration line in any block. The blocks are, in fact, sealed.
Improvement #3: block inheritance
The third step is to allow blocks to extends other blocks.
The idea is to be able to wrap a block with another one, creating an 'overloading' mechanism similar to the one used by OOP inheritance where methods are 'fall back' to the extended class if the extending class doesn't implement them.
Let us supposed we have the following block (very simple):
block A implements http://mystuff.org/skin/1.1
/stylesheets/changes2document.xslt
/stylesheets/faq2document.xslt
/stylesheets/document2html.xslt
/resources/logo.gif
and let us suppose that we want to change the look and feel of that block. The first two stylesheets provide simply a way to adapt from more specific markup to the Document DTD. So, my block would need to change only the last two resources 'document2html.xslt' and 'logo.gif'.
The best solution is to allow my block to explicitly "extend" that block and inherits the resources that it doesn't contain.
block b extends block a
/stylesheets/document2html.xslt
/resources/logo.gif
but then block B still is considered implementing behavior http://mystuff.org/skin/1.1 because the rest is inherited.
This mainly:
- reduces block development and maintanance costs because changes and bugfixes are directly inherited by all the extending blocks, thus allowing better SoC between the two groups mainaining the different blocks
- easy customization: blocks can be adapted for personal specific needs simply with a wrapper around and without the need to repackaging.
Part 2: technical details
Ok. Now that we have described where we want to go, let's describe how.
Cocoon Blocks
A Cocoon block is a zipped archive, just like JARs and WARs.
The suggested extension of a cocoon block is ".cob" (for COcoon Block).
The suggested MIME type is "application/x-cocoon-block".
A Cocoon Block (COB from now on) includes a directory called
/BLOCK-INF
which contains all the block metadata and the resources that must not be directly referentiable from other blocks (for example, jars, classes or file resources made available thru the classloader). The directories
/BLOCK-INF/classes /BLOCK-INF/jar
are used for classes and jar files. (This follows the WAR paradigm)
The main COB descriptor file is found at
/BLOCK-INF/block.xml
This file contains markup with a cob-specific namespace and will include the following information:
- block implementation metadata:
- unique URI identifier (this identifier will also be used as an address on where to locate the block and how to download it from the web!) (example: http://mystuff.org/dist/myblock-1.5.34.cob)
- version (1.5.34)
- short name (My Block)
- description
- author
- URI of license (http://mystuff.org/dist/license)
- URI of the distribution location (http://mystuff/dist/latest/myblock.cob)
- ???
- role(s):
- the URI(s) of the behavioral role(s) this block implements and exposes (optional)
- dependencies:
- the URI(s) of the behavioral roles this block expects, along with the prefixes used by the block as shortcuts in protocol resolving (see below for the meaning of this) (optional)
- inheritance:
- the URI of the block extended. (optional)
- sitemap:
- the location inside the block file space of the sitemap (optional, if not found defaults to '/sitemap.xmap')
- configurations:
- the configurations required for this block to function (optional)
Also, the /BLOCK-INF/ directory contains the 'roles' file for Avalon components:
/BLOCK-INF/roles.xml
Possible use-case scenario
Suppose you have your naked cocoon running in your favorite servlet container, and you want to deploy myblock.cob. Here is a possible sequence of actions on an hypotetical web interface on top of Cocoon (a-la Tomcat Manager)
- upload the myblock.cob to Cocoon
- Cocoon scans /BLOCK-INF/, reads block.xml and finds out the behaviors this block depends on as well as the block that it extends.
- the block manager connects to the uber "Cocoon Block Librarian" web service (hosted probably on cocoon.apache.org) and asks for the list of blocks that exhibit that required behavior.
- the librarian returns a list of those blocks, so the users chooses, or the manager allows the user to deploy its own block that implements the required behavior or to reuse those already deployed blocks that implement the required behaviors.
- Cocoon checks that all dependencies are met, then unpacks and installs the blocks
- For each block that exposes a sitemap, the deployment manager asks the deploying user where he/she wants to *mount* that block in the managed URI space or if he/she wants to keep them internal only (thus only available to the other blocks, but not mounted on the public URI space)
- for each block that requires installation-time configurations, the block manager will present the user information on how to configure the block.
- If no collisions in the URI spaces are found, the blocks are made available for servicing.
Resource dereferencing
Security concerns aside, the above scenario shows one major issue: blocks are managed, deployed and mounted by the container. There is (and there should not be) a way for a block to directly access another block because this would ruin IoC.
So, one block doesn't know where the blocks it depends on are located, both on disk *and* on the URI space as well.
The proposed solution is to use block-specific protocols to identify the dereferenced resources.
For example, the myblock.cob/sitemap.xmap file could contain a global matcher which works like this:
<map:match pattern="**/*.html">
<map:generate src="{1}.xml"/>
<map:transform src="block:skin:/stylesheets/document2html.xslt"/>
<map:serialize/>
</map:match>
please note the
block:skin:/stylesheets/document2html.xslt
which indicates
- block
-> use the block protocol
- skin
-> use the 'skin' prefix to lookup the block behavior URI and thus the block which implements it for this block (the block manager knows this)
- /stylesheets/document2html.xslt
-> it will ask the sitemap of the skin block to produce that resource.
Dereferencing navigation
Not only a sitemap needs to connect to the resources contained in the blocks on which the block depends on, but the resulting pages as well.
In fact, suppose you have a block that exposes a web service and another one that exposes a web application that wraps that web service. For sure, the generated web page will have to have a URI to connect to that service, since it's the client's browser that makes the call (unless we want to virtualize everything thru the sitemaps, but I wouldn't suggest it).
So, a possible solution is to use the "block:" protocol in the pages as well and have a URI-mapping transformer right before the serialization stage.
For example, things like
<form action="block:web-service:/post">...</form>
is transformed into
<form action="/servizio-web/post"/>...</form>
Some design decision taken
NO BEHAVIOR VALIDATION
I thought a lot about it but I think that having 'behavior description languages' (such as the WSDL-equivalent for blocks) is going to be terribly complicated, expensive to implement and hard to use and enforce, even for simple blocks which don't expose a sitemap and are just repositories for informations.
For this reason, there is no validation taking place: if a block implements a particular behavior and exposes it thru its descriptor file, Cocoon automatically assume it implements the behavior correctly.
In the future, we might think of adding a behavior description layer to enforce a little more validation, but I fear the complexity (for example) of validating stylesheets against a particular required behavior.
IMO, only human try/fail and patching will allow interoperability.
VERSIONING AS PART OF THE BEHAVIOR URI
The behavior URI *MUST* terminate with a /x.y that indicates the major.minor version of the behavior that a block implements.
On dependencies, each block must be able to specify the 'ranges' of versioning that it is known to work with. For example
<block behavior="http://xml.apache.org/forrest/skin/1.x" prefix="skin"/>
But I haven't really thought about the patterns that could be used for this.
Please, help on this.
CROSS-BLOCK SECURITY
Even I don't think anybody is stupid enough to use a single Cocoon instance to run a full ISP and ask for sandboxing of the single blocks, cross-block security is a big concern, expecially since you might be deploying components on the fly in a binary format.
So, first thing is to protect the /BLOCK-INF/ directory.
The second thing is to wrap each block with its own classloader, connected to the block dependency map, so that each class discovery is done only on the class space of the dependent blocks.
NOTE: this doesn't prevent people from using blocks as trojans, but we won't host blocks which don't come with the source code so we solve that problem.
COCOON MANAGER SECURITY
The cocoon manager might be a block itself that connects to specific cocoon internals and provides a web interface for it. So, it can be removed or disabled when put on production.
Also, the feature of automatic discovery of blocks thru the 'cocoon block library' can be turned off or substituted with its own (even the 'cocoon block library' could be a block, so you could have your own block library on your system instead of connecting to the apache one).
OPTIONAL COP
The block.xml file makes it *optional* to expose behaviors or to depend on them. This allows the COP model to nicely downgrade to the good old single-archive WAR paradigm for those who don't care about block polymorphism.
Conclusions
I think I have exposed a detailed plan on how to implement blocks and solve a number of issues we are having:
- allow users to 'compose' Cocoon only with those modules they need
- allow users to easily deploy their stuff on cocoon
- allow users to easily reuse web applications components without sacrificing coherence, interoperability and easy extensibility
- allow users to be helped by Cocoon to 'fill the gaps' and be suggested on what components is best required and feed it automatically (apt-get like)
- allow the Cocoon communities to clearly separate concerns between the core and the application-level stuff, thus allowing the cocoon community to really scale by massive development parallelization
- allows, for the first time in the history of the web, to use polymorphism, inheritence and COP at a web application level.
THANKS
I would like to thank Giacomo Pati and Carsten Ziegler for their great contribution and precious feedback.
----
CODA
Changes from version 1.0
- added the concept of block inheritance
- wrote a scenario for introduction of the COB model as an evolution of
the WAR model.
- added configurations to blocks
- changed block.info and blocks.roles into block.xml and roles.xml
- removed issues already identified by the first round of design
TODO
Blocks should allow to depend on 'ranges' of behavior versions.
Let's try to come up with a way to describe those ranges effectively.
The block manager should present the user with a form on how to configure the block
... thus the block should contain enough configuration metadata (default values, valid entries, ect..) to tell the block manager how to create the form to present. Should we use RDF for this or schemas are good enough?
Which avalon container should we use?
The one we currently use (ECM) is not powerful enough. Is there already a container which is powerful enough to handle our needs as described here? if not, what do we do? we implement our own or work with the avalon people to fix theirs to meet our needs?
How do we implement the block manager?
should it be a command line interface or a web interface, or both? what about security?
The 'uber library of cocoon blocks'
Where do we host it? how to we manage it? How do we provide the block discovery web service? which technology do we use: SOAP or REST?
Should we "digitally sign" our blocks? if so, how?
Reader's comments
Please don't change the content above this line, except if you want to add footnotes reference.
Question on versioning
Why mustthe behavior URI terminate with a /x.y that indicates the major.minor version of the behavior that a block implements? If each block must be able to specify the 'ranges' of versioning that it is known to work with, then why not adopt the functionality offered by XML attributes. For example:
<block behavior="http://xml.apache.org/forrest/skin" start="1.x" end="2.0" prefix="skin"/>
How to do a first microstep towards the goal.
Taken froma mail from SylvainWallez on cocoon-dev.
> > The TreeProcessor works by creating an evaluation tree of > ProcessingNodes corresponding to sitemap statements. It asks a > TreeBuilder to create this tree and then handles requests with it. > > The TreeBuilder reads the sitemap file (in an Avalon Configuration > object) and builds this tree by invoking a ProcessingNodeBuilder for > each element encountered in the sitemap. The ProcessingNodeBuilder in > turn creates an appropriate ProcessingNode that will be used at runtime > to "execute" the sitemap > > The ProcessingNode isn't created directly from the sitemap element, > since some sitemap elements don't always lead to identical processing > depending either on their attributes (e.g. <map:call resource=""> and > <map:call function="">) or the used components (e.g. <map:match> which > is different for regular Matcher and PreparedMatcher). > > The DefaultTreeBuilder has a createComponentManager() method that > creates - guess what? - the CM that is to be used within the processing > tree to lookup components. In that default implementation, this is just > the "current" one (i.e. the one passed to "compose()"). > > But if you look at SitemapLanguage, which is a subclass of > DefaultTreeBuilder, you will notice that its createComponentManager() > method creates a new CocoonComponentManager and configures it with > <map:components>. So <map:components> defines components of the sitemap > just a cocoon.xconf defines them for the Cocoon object. > > Adding a custom classloader to the sitemap to handle blocks should thus > be just a matter of giving that custom classloader to the created CM. >
CommandLine
- TARGET-AUDIENCE: beginner*advanced expert\\ - COCOON-RELEASES: 2.0.4\\ - DOCUMENT-STATUS: draft*reviewed released\\ ----
The Cocoon Command Line Interface
The Cocoon command line interface allows static versions of Cocoon web sites to be created.
It can follow links within pages (not just HTML) and adapt filename extensions to match the page's mime type.
1. Using the Cocoon Command Line Interface
The command line interface is accessible via the java class org.apache.cocoon.Main. However, in order to use it, various things must be set up (e.g. environment variables and a Java class path).
To make its use easier, a DOS batch file (run.bat) and a Unix shell script (run.sh) are available. At present, these files are only available with the source distribution, but they can be used with modification with the binary distribution. They have been created with the assumption that you build Cocoon from the sources. If you want to use the binary distribution, you will need to alter various paths within these files.
Alternative versions have been provided near the end of this page for both Unix and Windows.
[[NOTE: Couldn't we have a copy of these scripts in the binary distribution, all correctly configured?]]
To use the command line interface, you do not have to have a servlet engine (e.g. Tomcat) running.
2. Command line parameters
You can get a listing of the parameters on unix with:
./run.sh -h
or on Windows with:
run.bat -h
Which should look something like this:
------------------------------------------------------------------------
Apache Cocoon 2.0.4
Copyright (c) 1999-2002 Apache Software Foundation. All rights reserved.
------------------------------------------------------------------------
Usage: java org.apache.cocoon.Main [options] [targets]
Options:
-b, --brokenLinkFile <argument>
send a list of broken links to a file (one URI per line)
-f, --uriFile <argument>
use a text file with uris to process (one URI per line)
-h, --help
print this message and exit
-v, --version
print the version information and exit
-k, --logKitconfig <argument>
use given file for LogKit Management configuration
-l, --Logger <argument>
use given logger category as default logger for the Cocoon e
ngine
-u, --logLevel <argument>
choose the minimum log level for logging (DEBUG, INFO, WARN,
ERROR, FATAL_ERROR) for startup logging
-c, --contextDir <argument>
use given dir as context
-d, --destDir <argument>
use given dir as destination
-w, --workDir <argument>
use given dir as working directory
-P, --precompileOnly
generate java code for xsp and xmap files
-a, --userAgent <argument>
use given string for user-agent header
-p, --accept <argument>
use given string for accept header
-r, --followLinks <argument>
process pages linked from starting page or not (boolean argu
ment is expected, default is true)
-C, --configFile <argument>
specify alternate location of the configurationfile (default
is ${contextDir}/cocoon.xconf)
Note: the context directory defaults to './webapp'
3. Directories
- The context directory for Cocoon is the directory containing the root sitemap and the WEB-INF folder (usually
$TOMCAT_HOME/webapps/cocoon/).
- The config file will be
$contextDir/WEB-INF/cocoon.xconf
- The work directory can be anywhere you choose, it is where Cocoon will store various internal files generated whilst producing your site. Tomcat uses $TOMCAT_HOME/work/localhost/cocoon/ as its work directory. You could use the same. [[IS THIS CORRECT??]]
- The destination directory is where your site files will be placed when generated.
- The broken link file is used to record any links that were found that could not be successfully followed.
If either the work or destination directories do not exist, they will be created for you.
4. URIs and URI files
All parameters provided to the command line without a switch will be taken as the URI of a page to be generated. Alternatively, the -f switch can be used to provide a URI filename. URIs should be listed in this file one per line. Comments are not currently allowed and blank lines should be avoided.
The URIs provided should be just the part that is handled by Cocoon. So, if you wanted to generate the page that would be accessed as http://localhost/cocoon/mysite/mypage.html, your URI would be mysite/mypage.html.
5. Following Links
The command line interface has powerful functionality for following links within pages, using the -rswitch. Links are identified from within the pipeline itself, and thus can be followed in any document that has href, src or xlink:link somewhere within the pipeline.
To achieve this, the command line uses a combination of the link serializer and the 'links' view.
The 'links' view is set up within the <views> section of the sitemap. It's definition is like so:
<map:views> <map:view from-position="last" name="links"> <map:serialize type="links"/> </map:view> </map:views>
If this definition is in your root sitemap, you do not need it in your sub-sitemaps.
This definition must come before the pipelines in your sitemap.
To see the links within one of your pages using the link view, append ?cocoon-view=links to your URL (or &cocoon-view=links if there is already a '?' in your URL).
6. Mime Type Checking
The command line interface can rewrite URLs for pages to make them suitable for saving in files.
To achieve this, Cocoon first identifies the mime type of a generated page and checks that the URL has an appropriate file extension. If it doesn't, it adds one.
It then replaces " with ', ? and : with _, and adds a default filename if the URL ends with /.
Cocoon also correctly rewrites the links within your pages, so that your link hierarchy is correctly maintained.
7. Multiple Page Renderings
In order to follow links and rewrite URLs, Cocoon must generate pages multiple times:
- once to extract links from the page, using the links view
- once to check the mime type for URL rewriting
- once for getting page contents
In Cocoon 2.1, in certain circumstances, the number of page generations can be reduced, as described below.
If link following is not required, the links view will not be generated.
If no URLs need rewriting (i.e. they all have the correct file extensions, and none end in /), the **** switch can be used to switch off URL rewriting, thus preventing the generation of the page to get its mime type. [[NOTE: no switch is available to give access to the confirmExtension option within the code]]
8. Logging
I have not explored the Cocoon logging functionality and can therefore add no more than is given by Cocoon help:
-k, --logKitconfig <argument>
use given file for LogKit Management configuration
-l, --Logger <argument>
use given logger category as default logger for the Cocoon e
ngine
-u, --logLevel <argument>
choose the minimum log level for logging (DEBUG, INFO, WARN,
ERROR, FATAL_ERROR) for startup logging
In Cocoon 2.1, the -V switch can be used to switch on 'verbose' output to the console window.
The Log Kit Config parameter is a path to the logkit.xconf file. The logger parameter refers to the logging category to be used, e.g. 'cli'. The logLevel defines how much logging is to be done (as described above).
9. Precompiling XSPs
Using the 'precompile only' switch, Cocoon will only precompile XSPs who's URIs were given as command line parameters, and will not generate any content. [[IS THIS CORRECT?]]
10. Precompiling Sitemaps
Before generating any pages, Cocoon will compile its sitemaps and XSP pages. [[IS THIS CORRECT?]]
11. Agent Options
Using 'agent options', the command line can be made to emulate specific browsers. This is particularly useful when your site serves different content depending upon the user's browser (otherwise known as 'user agent').
12. Accept Options
When requesting pages, browsers can inform the server what kinds of content they can accept (e.g. "this browser can handle 'image/png'"). If your site returns different pages depending upon browser capabilities, you may want to use this option.
13. Sample Unix Script
Below is a simple unix script which adds every .jar file it finds into the classpath automatically.
[[NOTE: This script has only been tested with Java 1.4]]
#!/bin/sh
# Portions copyright (c) 2001-2002 The Apache Software Foundation. All rights reserved.
for i in WEB-INF/lib/*.jar
do
# if the directory is empty, then it will return the input string
# this is stupid, so case for it
if [ "$i" != "WEB-INF/lib/*.jar" ] ; then
if [ -z "$LOCALCLASSPATH" ] ; then
LOCALCLASSPATH=$i
else
LOCALCLASSPATH="$i":"$LOCALCLASSPATH"
fi
fi
done
if [ -z "$TOMCAT_HOME" ] ; then
TOMCAT_HOME=$CATALINA_HOME
fi
if [ ! -z "$TOMCAT_HOME" ] ; then
LOCALCLASSPATH="$TOMCAT_HOME/common/lib/servlet.jar":"$LOCALCLASSPATH"
fi
java -classpath $LOCALCLASSPATH org.apache.cocoon.Main $*
For Java 1.3, try adding:
LOCALCLASSPATH="$LOCALCLASSPATH":"%TOMCAT_HOME%\common\lib\xml-apis.jar" LOCALCLASSPATH="$LOCALCLASSPATH":"%TOMCAT_HOME%\common\lib\%TOMCAT_HOME%\common\lib\xalan-2.3.1.jar" LOCALCLASSPATH="$LOCALCLASSPATH":"%TOMCAT_HOME%\common\lib\%TOMCAT_HOME%\common\lib\batik-all-1.5b1.jar" LOCALCLASSPATH="$LOCALCLASSPATH":"%TOMCAT_HOME%\common\lib\xercesImpl-2.0.0.jar"
Note: This has Java 1.3 tweak has not been tested.
14. Sample Windows Batch File
Note: this has been tested on Cocoon 2.0.4 using Java 1.3 and Tomcat 4.0.3.
@echo off cls :: ----------------------------------------------------------------------------- :: run.bat - Win32 Run Script for Apache Cocoon :: :: $Id: run.bat,v 1.3 2002/03/06 15:44:57 nicolaken Exp $ :: ----------------------------------------------------------------------------- :: ----- Verify and Set Required Environment Variables ------------------------- if not "%JAVA_HOME%" == "" goto gotJavaHome echo You must set JAVA_HOME to point at your Java Development Kit installation goto cleanup :gotJavaHome :: ----- Verify and Set Tomcat Location (UV 10/1/03) --------------------------- if not "%TOMCAT_HOME%" == "" goto gotTomcatHome echo You must set TOMCAT_HOME to point to your Tomcat installation goto cleanup :gotTomcatHome :: ----- Verify and Set Required Environment Variables ------------------------- if not "%COCOON_LIB%" == "" goto gotCocoonLib set COCOON_LIB=.\WEB-INF\lib :gotCocoonLib if not "%COCOON_WORK%" == "" goto gotCocoonWork set COCOON_WORK=.\work :gotCocoonWork :: ----- Set Up The Runtime Classpath ------------------------------------------ set CP=%JAVA_HOME%\lib\tools.jar;%COCOON_WORK% call appendcp.bat %COCOON_LIB%\avalon-framework-20020627.jar call appendcp.bat %COCOON_LIB%\cocoon-2.0.4.jar call appendcp.bat %COCOON_LIB%\cocoon-scratchpad.jar call appendcp.bat %COCOON_LIB%\excalibur-logger-20020820.jar call appendcp.bat %COCOON_LIB%\excalibur-sourceresolve-20020820.jar call appendcp.bat %COCOON_LIB%\excalibur-component-20020916.jar call appendcp.bat %COCOON_LIB%\excalibur-pool-20020820.jar call appendcp.bat %COCOON_LIB%\excalibur-cli-1.0.jar call appendcp.bat %COCOON_LIB%\commons-logging-1.0.jar call appendcp.bat %COCOON_LIB%\logkit-20020529.jar call appendcp.bat %COCOON_LIB%\excalibur-collections-20020820.jar call appendcp.bat %COCOON_LIB%\excalibur-instrument-20021108.jar call appendcp.bat %COCOON_LIB%\excalibur-monitor-20020820.jar call appendcp.bat %COCOON_LIB%\excalibur-xmlutil-20020820.jar call appendcp.bat %COCOON_LIB%\commons-jxpath-1.0.jar call appendcp.bat %COCOON_LIB%\pizza-1.1.jar call appendcp.bat %COCOON_LIB%\velocity-1.2.jar call appendcp.bat %COCOON_LIB%\excalibur-datasource-vm12-20021121.jar call appendcp.bat %COCOON_LIB%\commons-collections-2.1.jar call appendcp.bat %COCOON_LIB%\resolver-20020130.jar :: Removed because the classpath it generates is too long: rem for %%i in (WEB-INF\lib\*.jar) do call appendcp.bat %%i call appendcp.bat %TOMCAT_HOME%\common\lib\servlet.jar :: Required for Java 1.3 call appendcp.bat %TOMCAT_HOME%\common\lib\xml-apis.jar call appendcp.bat %TOMCAT_HOME%\common\lib\xercesImpl-2.0.0.jar call appendcp.bat %TOMCAT_HOME%\common\lib\xalan-2.3.1.jar call appendcp.bat %TOMCAT_HOME%\common\lib\batik-all-1.5b1.jar :: ----- Run Cocoon ------------------------------------------------------------ if "%OS%" == "Windows_NT" goto nt %JAVA_HOME%\bin\java.exe %COCOON_OPTS% -classpath %CP% org.apache.cocoon.Main %1 %2 %3 %4 %5 %6 %7 %8 %9 :nt %JAVA_HOME%\bin\java.exe %COCOON_OPTS% -classpath %CP% org.apache.cocoon.Main %* :: ----- Cleanup the environment ----------------------------------------------- :cleanup set CP=
Save this as run.bat in the $TOMCAT_HOME\webapps\cocoon folder, and run it with
run -c . -C WEB-INF/cocoon.xconf -u ERROR YOUR-URI
If you find that it gives an error of:
Exception in thread "main" java.lang.NoClassDefFoundError:.....
look for a jar file in the WEB-INF/lib folder that is likely to contain the class that was not found, and add a line call appendcp.bat %COCOON_LIB%\<JAR-FILENAME>to the above script.
[[Note, this script mentions each jar file by name, rather than including all jars in the WEB-INF/lib folder because doing the latter would make a classpath that is too long for a DOS prompt. Does anyone know how to get DOS to accept a longer environment variable?]]
15. Sample from Cocoon's own build.xml file
Cocoon uses itself in command-line mode to generate its documentation. Studying the build.xml file that comes with the Cocoon source gives a very concrete example of how to use the command-line mode.
Here's the relevant build.xml section from the current source code. Although this build information is meant for ant, it is fairly self-explanatory.
<java classname="org.apache.cocoon.Main" fork="true"
dir="${build.context}"
failonerror="true" maxmemory="128m">
<arg value="-c."/>
<arg value="-d../docs"/>
<arg value="-w../work"/>
<arg value="-b../brokenlinks.txt"/>
<arg value="-k../documentation/logkit.xconf"/>
<arg value="-u${build.docs.loglevel}"/>
<arg value="-V"/>
<arg value="index.html"/>
<classpath>
<path refid="classpath"/>
<fileset dir="${build.dir}">
<include name="*.jar"/>
</fileset>
<pathelement location="${tools.jar}"/>
<pathelement location="${build.context}/WEB-INF/classes"/>
</classpath>
</java>
Some Keywords
CLI, offline generation
ConfiguringTheLogs
Cocoon Logging Configuration
This document describes how to configure Cocoon's logging mechanism. The first section describes the basics of logging, introducing the key concept of Cocoon's logging. The next section describes the configuration files involved in Cocoon's logging.
Basics
Cocoon's logging mechanism introduces following keywords
- Category is a symbolic name of some sort of logging output.
- Target specifies a physical logging output
- LogEvent created by a logging source, and routed via category resolution to a logging target, the logging sink
- Formatter formats a logging event
Following category names are declared in the cocoon.xconf file
- sitemap
- core.xml-parser
- core.store.transient
- core.store.janitor
- core.xslt-processor
- core.xpath-processor
- core.url-factory
- core.source-handler
- core.program-generator
- core.jsp-engine
- core.i18n-bundles
- core.xscript
- core.language.java
- core.language.js
- core.classloader
- core.markup.xsp
- core.xml-serializer
- core.xml-deserializer
- core.monitor
- core.resolver
A log target is a physical logging destination. A log target is wrapped in a LogTargetFactory, and declared in the logkit.xconf file.
Following log target factories are available
- AsyncLogTargetFactory a virtual target, forwarding log events in a separate thread
- CocoonTargetFactory an enhanced FileTarget, specially tailored for Cocoon needs
- DatagramTargetFactory forwards logging events to datagram sink
- FileTarget write logging events to file sink, offers rotation features
- JDBCTargetFactory writes logging events to JDBC sink
- JMSTargetFactory writes logging events to JMS sink
- PriorityFilterTargetFactory a virtual target, filtering log events by its priority
- SMTPTargetFactory forwards logging events as emails
- ServletTargetFactory forwards formatted log events to the Servlets logging mechanism
- SocketTargetFactory forwards logging events to socket sink
- StreamTargetFactory writes logging events to a java.io.OutputStream, especially to System.out, and System.err
Configuration
Logging categories are used in the java sources. A java programmer decides to use a specific category in some piece of java code.
Alternatively Avalon components used in Cocoon specifies in the its declaration cocoon.xconf the category by the attribute logger.
Note that in both cases we deal only with logging categories, not with logging targets. Logging targets are completely independent from the logging category, in respect of the java programmer.
In next level logkit.xconf maps logging categories to a log target. The configuration file logkit.xconf defines the log targets. More specific, logkit.xconf declares first LogTargetFactories. Next it configures instances of LogTargetFactory objects. And finally it maps logging categories to LogTargetFactory instances.
You have following mapping options
- Map a single category to a single logging target
- Map more than one category to a single logging target
- Map a child category to different logging target
- Define a virtual logging target filtering logging events by priority, and map it to the logging target
Each logging target has some target specific configuration options. Most of the logging targets offer a logging formatting configuration, which are described below.
The configuration file WEB-INF/web.xmlis only used during startup and until the logkit.xconftakes over.
Declaring LogTargets
Before you can configure a specific log target you have to declare it. This is done in the logkit.xconf file.
Inside of the factorieselement each factoryelement declares a log target.
A factory element requires following attributes:
- type - defining the name of the element used for defining this log target instance
- class - specifies the full qualified class name of the LogTargetFactory
Example
This example declares the SerlvetTargetFactory, you may use servletfor defining a servlet log target in the following sections of the logkit.xconf file.
....
<factories>
<factory type="servlet"
class="org.apache.avalon.excalibur.logger.factory.ServletTargetFactory"
/>
...
</factories>
<targets>
...
<servlet...
....
</servlet>
...
</targets>
....
AsyncLogTargetFactory
The configuration of a AsyncLogTargetFactory accepts following attributes:
- id attribute specifies the target name, used in the category definition for referring to this target.
- queue-size attribute for specifying the size of queue of log events.
- priority the thread priority of the AsyncLogTarget thread, you may specify MIN, MAX, NORM, or a thread priority integer number, by default the priority is set to 1.
The AsyncLogTargetFactory expects an valid target factory configuration as its child element.
Example
The following configuration template configures a AsyncLogTarget
<async-target id="async-target-id" queue-size="15" priority="MIN"> ... any-target-definition ... </async-target>
CocoonTargetFactory
The configuration of a CocoonTargetFactory accepts following attributes:
- id attribute specifies the target name, used in the category definition for referring to this target.
The configuration of a CocoonTargetFactory accepts following sub elements:
- filename
- format
- append
- rotation
Example
The following configuration template configures a CocoonTarget, writing log events using the Cocoon's context root directory, as its base directory. (If Cocoon cannot access its context root directory Cocoon sets context-root to the sub-directory log of its work-dir.)
<cocoon id="core">
<filename>${context-root}/WEB-INF/logs/core.log</filename>
<format type="cocoon">
%7.7{priority} %{time} [%{category}] (%{uri})
%{thread}/%{class:short}: %{message}\n%{throwable}
</format>
<append>true</append>
</cocoon>
DatagramTargetFactory
The configuration of a DatagramTargetFactory accepts following attributes:
- id attribute specifies the target name, used in the category definition for referring to this target.
The configuration of a DatagramTargetFactory accepts following sub elements:
FileTarget
The configuration of a CocoonTargetFactory accepts following attributes:
- id attribute specifies the target name, used in the category definition for referring to this target.
The configuration of a CocoonTargetFactory accepts following sub elements:
- filename
- format
- append
- rotation
Example
The following configuration template configures a FileTarget, writing logs to the file /var/tmp/log1.log.
<file>
<filename>/var/tmp/log1.log</filename>
<append>false</append>
<format type="pattern">
%7.7{priority} %{time} [%{category}] : %{message}\n%{throwable}
</format>
</file>
JDBCTargetFactory
The configuration of a JDBCTargetFactory accepts following attributes:
- id attribute specifies the target name, used in the category definition for referring to this target.
The configuration of a JDBCTargetFactory accepts following sub elements:
- datasource specify the JNDI data source name
- normalized specify true, or false.
- table specify the name of the table using the attribute name
Setting normalized to true will use the NormalizedJDBCTarget, otherwise the DefaultJDBCTarget is used.
The DefaultJDBCTarget writes each log event into the table specified by the attribute name of element table.
The NormalizedJDBCTarget writes normalized value for category, and priority.
Subelements of table
- category specify the column name as its value,
- static specify the column name as its value, use the attribtue aux to specify the static value
- context specify the column name as its value, use the attribute aux to specify the context entry name.
- message specify the column name as its value
- priority specify the column name as its value
- time specify the column name as its value
- rtime specify the column name as its value
- throwable specify the column name as its value
- hostname specify the column name as its value
Example
The following configuration template configures a JDBC target, writing to table cocoonlog. It uses the JDBC datasource jdbc/logdb. The category value of the log event is written to the column cat of table cocoonlog. The message value of the log event is written to the column msg of table cocoonlog. The priority value of the log event is written to the column prio of table cocoonlog. The time value of the log event is written to the column when of table cocoonlog. The context entry uri is writting to column uri.
<database>
<datasource>jdbc/logdb</datasource>
<normalized>false</normalized>
<table name="cocoonlog">
<category>cat</category>
<message>mesg</message>
<priority>prior</priority>
<time>when</time>
<context aux="uri">uri</context>
</table>
</database>
JMSTargetFactory
The configuration of a JMSTargetFactory accepts following attributes:
- id attribute specifies the target name, used in the category definition for referring to this target.
The configuration of a JMSTargetFactory accepts following sub elements:
PriorityFilterTargetFactory
The configuration of a CocoonTargetFactory accepts following attributes:
- id attribute specifies the target name, used in the category definition for referring this target.
- log-level attribute specifies the filter priority level. Logevents of this level or higher are passed further to the enclosed target factory configuration. The log-level sorted from lower priority to highest priority:
- DEBUG priority defintion 5
- INFO priority defintion 10
- WARN priority defintion 15
- ERROR priority defintion 20
- FATAL ERROR priority defintion 25
The PriorityFilterTargetFactory expects an valid target factory configuration as its child element.
Example
The following configuration template configures a PriorityLevelTarget
<priority-target id="priority-target-id" log-level="ERROR"> ... any-target-definition ... </priority-target>
SMTPTargetFactory
The configuration of a SMTPTargetFactory accepts following attributes:
- id attribute specifies the target name, used in the category definition for referring to this target.
- context-key attribute specifies the context key, storing the required JavaMail Session object, by default the context-key session-context is used.
The configuration of a SMTPTargetFactory accepts following sub elements:
- to one or more recipients addresses
- from the sender address
- subject the subject value
- maximum-size defines the maximum numbers of logevents per mail
- format
ServletTargetFactory
The configuration of a SocketTargetFactory accepts following attributes:
- id attribute specifies the target name, used in the category definition for referring to this target.
- context-key attribute specifies the context key, storing the required
ServletContext object, by default the context-key attribute uses the context key servlet-context.
The configuration of a SocketTargetFactory accepts following sub elements:
- format
Example
The following configuration template configures a ServletTarget, expecting a ServletContext object available under context-key servlet-context.
<servlet id="servlet-target-id" context-key="servlet-context">
<format type="pattern">
%7.7{priority} %{time} [%{category}] : %{message}\n%{throwable}
</format>
</servlet>
SocketTargetFactory
The configuration of a SocketTargetFactory accepts following attributes:
- id attribute specifies the target name, used in the category definition for referring to this target.
The configuration of a SocketTargetFactory accepts following sub elements:
- address - the attribute hostname specifies the destination host address, and the port attribute spefies the detination port number.
StreamTargetFactory
The configuration of a StreamTargetFactory accepts following attributes:
- id attribute specifies the target name, used in the category definition for referring to this target.
The configuration of a StreamTargetFactory accepts following sub elements:
- stream specifies the stream, you may use following stream sink names
- System.out outputs log events to the System out stream
- System.err outputs log events to the System err stream
- format
Example
The following configuration template configures a StreamTarget
<stream-target id="stream-target-id">
<stream>System.out</stream>
<format type="pattern">
%7.7{priority} %{time} [%{category}] : %{message}\n%{throwable}
</format>
</stream-target>
Formatting
The following section describes the various formatting options available.
PatternFormatter
This formater formats the LogEvents according to a input pattern string.
The format of each pattern element can be
%[+|-][#[.#]]{field:subformat}
- The +|- indicates left or right justify.
- The #.# indicates the minimum and maximum size of output. You may omit the values and the field will be formatted without size restriction. You may specify #, or #. to only define the minimum size. You may specify .# to only define the maximum size.
- field indicates which field is to be output and must be one of properties of LogEvent. The following fields are currently supported:
- category Category value of the logging event.
- context Context value of the logging event.
- message Message value of the logging event.
- time Time value of the logging event.
- rtime Relative time value of the logging event.
- throwable Throwable value of the logging event.
- priority Priority value of the logging event.
- thread Name of the thread which logged the event.
- subformat indicates a particular subformat to use on the specified field, and is currently only supported by:
- context Specifies the context map parameter name.
- time Specifies the pattern to be pass to SimpleDateFormat to format the time.
A simple example of a typical PatternFormatter format would be:
%{time} %5.5{priority}[%-10.10{category}]: %{message}
This would produce a line like:
1000928827905 DEBUG [ junit]: Sample message
The format string specifies that the logger should first print the time value of the log event without size restriction, then the priority of the log event with a minimum and maximum size of 5, then the category of the log event right justified with a minimum and maximum size of 10, followed by the message of the log event without any size restriction.
ExtendedPatternFormatter
Formatter especially designed for debugging applications. This formatter extends the standard PatternFormatter to add two new possible expansions. These expansions are %{method} and %{thread}. In both cases the context map is first checked for values with specified key. This is to facilitate passing information about caller/thread when threads change (as in AsyncLogTarget). They then attempt to determine appropriate information dynamically.
CocoonLogFormatter
An extended pattern formatter. New patterns are defined by this class are :
- class outputs the name of the class that has logged the
message. The optional short subformat removes the package name. Warning : this pattern works only if formatting occurs in the same thread as the call to Logger, i.e. it won't work with AsyncLogTarget.
- thread outputs the name of the current thread (first element
on the context stack).
- uri outputs the request URI.
Logging Rotation
The logging rotation feature is restricted to FileTargets, and logging targets derived from a FileTarget.
The rotation concept introduce a file strategy for defining the filename, orthogonally to the file strategy, a rotate strategy decides the condition of rotation.
Following file strategies are available
- RevolvingFileStrategy write logging data to a fixed number of files
- attribute type=revolving
- attribute init specifies starting revolving index, by default it is 0
- attribute max specifies ending revolving index, by default it is 10
- UniqueFileStrategy, write logging data to unique named file
- attribute type=unique
- attribute pattern is SimpleDatePattern, by default it is
yyyy-MM-dd
- attribute suffix is optional, by default it is
.log
Following rotate strategies are available
- RotateStrategyByDate - rotate if the current date, is different from the starting date of logging, or the latest rotation, comparing is performed via formatting both date using the specifed SimpleDateFormat pattern
- RotateStrategyByTime - rotate after logging a period of time, specifed as SimpleDateFormat value of the format HH:mm:SS
- RotateStrategyBySize - rotate if size of logging data exceeds the specified size
- OrRotateStrategy - combining a rotate strategy, rotate if one of the enclosed rotate strategy wants to trigger a rotation
Specifying the rotation settings is done in the target section of a log target.
Specify a revolving file strategy using 4 files, rotate if writing more than 10 MB logging date, or an hour of day in year has expired. The files have suffix like 000001
...
<rotation type="revolving" init="1" max="4">
<or>
<size>10m</size>
<date>DD:HH</date>
</or>
</rotation>
...
Specifying a unique file strategy, and rotate if more than 100 MB logging data has been written, or after 5 minutes of logging. The files have suffix like 200211261127.log.
<rotation type="unique" pattern="yyyyMMddHHmm" suffix=".log">
<or>
<size>100m</size>
<time>00:05:00</time>
</or>
</rotation>
A short date pattern summary
- yyyy Year
- MM Month, MMM Month by Name
- DD Day in year
- dd Day in month
- HH Hour in day 0-23
- kk Hour in day 1-24
- mm Minute
- ss Second in minute
- SSS Milliseconds
Specifying logkit.xconf
Defining the logkit.xconf file you want to use depends on the Cocoon environment.
- Servlet environment, you specify a servlet init parameter logkit config, eg.
... <init-param> <param-name>logkit-config</param-name> <param-value>/WEB-INF/logkit.xconf</param-value> </init-param> ...
- Commandline environment, specify the option
-k, or --logKitconfig, eg.
java org.apache.Cocoon -k logkit.xconf ...
, or
java org.apache.Cocoon --logKitconfig logkit.xconf ...
Examples
This snippet configures a file rotation, rotating the file every 5 minutes, or if the file size exceeds 2MB. The filename suffixes represents the date and time of file creation, eg. access.log20021209 151231.log.
<logkit>
<factories>
<factory type="file"
class="org.apache.avalon.excalibur.logger.factory.FileTargetFactory"
/>
</factories>
<targets>
<file>
<filename>/tmp/access.log</filename>
<format pattern="extended">
%7.7{priority} %{time} [%{category}]: %{message}\n%{throwable}
</format>
<append>false</append>
<rotation type="unique" pattern="yyyyMMdd HHmmss" suffix=".log">
<or>
<size>2m</size>
<time>00:05:00</time>
</or>
</rotation>
</file>
</targets>
<categories>
<category name="my-cat-1" log-level="DEBUG">
<log-target id-ref="file"/>
</category>
</categories>
</logkit>
See Also
DistributingCocoonApplications
Random Thoughtsfrom Leigh Doddsabout how one might go about distributing a Cocoonapplication.
Scenario
Lets begin a thought experiment:
I'm building a web application based on the Cocoon architecture, which I'm intending to be given away as open source (of course!). I craft my application which ends up consisting of a number of SitemapPipelines, some XSLTstylesheets, assorted web pages and images, and perhaps a custom Componentor three.
How do I package up this application so that others can make use of it? What options do I have?
Options
The available options seem to be:
- Complete Webapp
-- a complete web application that is essentially a Cocoon distribution customised to run my application rather than the default set of examples.
- Donate to Cocoon
-- I could donate my application directly to the Cocoon project. It could then be added to the standard Cocoon distribution, perhaps initially in the scratchpad. That way everyone who downloads Cocoon will have my application.
- Cocoon Bundle
-- a zip/tar file consisting of just my application files, with instructions about how to configure an existing Cocoon instance to run my application, e.g. by mountingmy sitemap.
What are the cost/benefits of each scenario?
The Complete Webapp option seems to be the best if I want people not already running Cocoon to run my application. The application distro is obviously much bigger, and existing Cocoon users may just want to add it to an existing instance.
Donating to Cocoon has the obvious benefit that a lot more people will see the application: it will be highly visible. It will already be pre-configured, so there's less involved in setting it up. However this makes the Cocoon distro itself much more complex, especially for people who aren't interested in my applications functionality.
Using a Cocoon Bundle has the advantage that its easy for existing Cocoon users to deploy. But has the disadvantage that non-Cocoon users won't be able to easily use it.
Conclusions and Suggestions
We might conclude from this that if I want to distribute a Cocoon application as widely as possible then I just simply bundle it as a Complete Webapp. But it's also worth my while to take the time to produce a Cocoon Bundle and supporting installation docs.
Applying this thinking to Cocoon itself it seems that one good way to modularise the application is to simply modularise the distribution along these lines. There could be a 'complete' distro that simply has everything -- basically the current situation. However it would also be worth having an 'empty' distro that has nothing more than a Minimal Sitemap. There can then be separate Cocoon Bundles for additional self-contained functionality.
Another way to look at this idea is to think of Cocoon as a container, similar to a Servlet or EJB container: these application have a default install with little/no functionality, but often have separately downloadable examples. Application providers can bundle their applications for automatic deployment into a container.
Further Thoughts
Deploying a Cocoon Bundle could be an automated process. To mount the application I either need to:
- copy the applications additional pipelines into the root sitemap, or
- mount the application as a sub-sitemap
Both of these operations could be achieved with a simple XSLT stylesheet, possibly fronted by an Antscript. In the first case the stylesheet could just copy over the appropriate pipelines from one document to the other. In the second case, the stylesheet would just add the appropriate <map:mount>instructions [1] .
Imagine that this application deployment functionality is a standard part of Cocoon [2] . As an adminstrator I could use this functionality to automatically deploy (perhaps even download as well, cf: Debian package management) other peoples applications, those examples in which I'm interested, and even the documentation.
----
I've been mulling this over for a while, but the recent cocoon-dev discussions about modularising Cocoon (amongst other things) prompted me to actually write it down.
I'm in the process of writing a couple of Cocoon based applications, so wanted to get the options straight in my head. I prefer the Cocoon Bundle option myself
Comments very much appreciated.
-- Leigh Dodds
----
See also
Readers comments
Automounting simple applications-- Bertrand Delacretaz
In this second case, if the application consists only of a sitemap and static files, copying them to a new directory under the mount subdirectory of a standard Cocoon installation is sufficient (see UnderstandingCocoonMounts).
Requirements for auto-installing Bundles-- Bertrand Delacretaz
Here's a tentative list of what Cocoon needs to be able to auto-install Bundle XYZ:
- Add entries to the main sitemap, marking them as created by Bundle XYZ so they can be removed automatically. Should be able to query the user for variable values.
- Same for cocoon.xconf (or add "sub-xconf files" owned by Bundle XYZ).
- Add jar files to the appropriate directory, marking them as owned by Bundle XYZ so they can be removed automatically.
- Restart Cocoon or otherwise reload the new jar files
Instead of modifying the existing files, it might be easier to create a new XYZ subdirectory under WEB-INF, and put all the Bundle configuration and jar files there. This requires changes to the sitemap and xconf handling though.
An interesting use-case for this is installing a Bundle that accesses a database using a new JDBC driver.This requires changes to sitemap, xconf and a new jar, and database information provided by the user during installation.
----
From a not-the-smartest-user-in-the-universe perspective:
If I recieve a "Cocoon-app" from somewhere, I want to:
- drop it in a subdirectory / upload it via webdav to some directory I mean context.
Period. No restarts, no fiddling with the main site map, no configuration of the cocoon "kernel", only configuration of the new cocoon app.
Naturally one might have to do other configurations in the environment that the cocoon app might need, but that's a completely different story.
The important thing is that one should not have to risk to break anything in the existing cocoon installation, when installing new things.
/Ola Berg
---- What about "hot-deployment"? I think that the TreeProcessor and the ComponentManager used in Cocoon should be accesible and have hooks to an installer-thread. This thread could itself be a component that is bootstrapped by the CM. It listens for the bundle in the dropin subdirectory and automounts in .xmap and .xconf AND adds the component into the already running CM AND mounts the new sitemap components into the already running TreeProcessor (the last step shouldn´t be needed since the TreeProcessor checks for updates to the sitemap.)
/Mats Norén
EXistInCocoon
Integrating eXist into an existing Cocoon 2.0.4 installation
eXist comes with its own pre-installed Cocoon. However, integrating eXist into an already running Cocoon is not very difficult. Follow the steps bellow to integrate eXist 0.9 into an existing Cocoon 2.0.4 web application.
The following paragraphs assume that you have already set up a web application based on Cocoon.
First create a directory named data in the WEB-INF folder of your Cocoon based web application. This is where the database files will be created by eXist.
Next, copy the conf.xml configuration file from eXist into the WEB-INFfolder of your Cocoon application. Additionally, you have to copy a number of jar-files from eXist's WEB-INF/lib folder into the WEB-INF/lib folder of your Cocoon application:
- exist.jar
- antlr.jar
- axis.jar
- clutil.jar
- commons-collections-2.1.jar
- commons-discovery.jar
- commons-httpclient-20020423.jar
- commons-jxpath-1.0.jar
- commons-logging-1.0.jar
- jakarta-oro-2.0.6.jar
- jaxrpc.jar
- libreadline-java.jar
- log4j.jar
- saaj.jar
- trove.jar
- tt-bytecode.jar
- wsdl4j.jar
- xmldb.jar
- xmlrpc-1.1.jar
If you want WebDAV access you'll also need:
- jakarta-tomcat-4.0.3/webapps/exist/WEB-INF/lib/dom4j.jar
- jakarta-tomcat-4.0.3/webapps/exist/WEB-INF/lib/xincon.jar
- jakarta-tomcat-4.0.3/webapps/exist/WEB-INF/lib/catalina-util.jar
Next step: edit the web.xml file in your WEB-INF directory. From the web.xml file that comes with eXist copy the servlet definitions for the RpcServlet, DatabaseAdminServlet, AxisServlet, AdminServlet and (optionally) the xincon servlet into your own web.xml. Additionally, copy all the servlet-mapping sections for these servlets. Please note that order is important in the web.xml file: the block with <servlet> sections should always precede the <servlet-mapping> sections.
An example web.xml file is shown below:
Example: web.xml example (Cocoon CVS version 2.1 and eXist CVS version)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
This is the web-app configurations that allow Cocoon to work under
Apache Tomcat. Please, follow the installation section of the
documentation for more information about installing Cocoon on Tomcat
-->
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<display-name>eXist Server</display-name>
<description>eXist Server Setup</description>
<!-- Servlets required by eXist -->
<!-- RpcServlet provides XML-RPC access to eXist -->
<servlet>
<servlet-name>org.exist.xmlrpc.RpcServlet</servlet-name>
<servlet-class>org.exist.xmlrpc.RpcServlet</servlet-class>
</servlet>
<!-- DatabaseAdminServlet: used to start/stop the database. -->
<servlet>
<servlet-name>org.exist.DatabaseAdminServlet</servlet-name>
<servlet-class>org.exist.DatabaseAdminServlet</servlet-class>
<!-- where to find eXist's configuration file
relative to basedir
-->
<init-param>
<param-name>configuration</param-name>
<param-value>conf.xml</param-value>
</init-param>
<!-- eXist's home directory. All file names in
the configuration file will be relative to this
directory.
-->
<init-param>
<param-name>basedir</param-name>
<param-value>WEB-INF/</param-value>
</init-param>
<init-param>
<param-name>start</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- configure the Axis servlet. Axis provides eXist's
web-services via SOAP -->
<servlet>
<servlet-name>AxisServlet</servlet-name>
<display-name>Apache-Axis Servlet</display-name>
<servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>AdminServlet</servlet-name>
<display-name>Axis Admin Servlet</display-name>
<servlet-class>org.apache.axis.transport.http.AdminServlet</servlet-class>
<load-on-startup>100</load-on-startup>
</servlet>
<!-- xincon: Provides Webdav for eXist -->
<servlet>
<servlet-name>xincon</servlet-name>
<servlet-class>xincon.WebdavServlet</servlet-class>
<init-param>
<param-name>dbroot</param-name>
<param-value>xmldb:exist:///db</param-value>
</init-param>
<!-- log4j parameters -->
<init-param>
<param-name>log4j-priority</param-name>
<param-value>DEBUG</param-value>
</init-param>
<init-param>
<param-name>log4j-layout</param-name>
<param-value>%-5p [%M] %m%n</param-value>
</init-param>
<!-- end log4j parameters -->
<!-- load this servlet first to avoid conflicts with
eXist's log-configuration -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Configure Cocoon2 -->
<servlet>
<servlet-name>Cocoon2</servlet-name>
<display-name>Cocoon2</display-name>
<description>The main Cocoon2 servlet</description>
<!-- I'm skipping Cocoon configuration here ... -->
</servlet>
<servlet-mapping>
<servlet-name>org.exist.xmlrpc.RpcServlet</servlet-name>
<url-pattern>/xmlrpc</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>org.exist.DatabaseAdminServlet</servlet-name>
<url-pattern>/admin</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>xincon</servlet-name>
<url-pattern>/webdav/*</url-pattern>
</servlet-mapping>
<!-- Axis servlets -->
<servlet-mapping>
<servlet-name>AxisServlet</servlet-name>
<url-pattern>servlet/AxisServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>AxisServlet</servlet-name>
<url-pattern>*.jws</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>AxisServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>AdminServlet</servlet-name>
<url-pattern>servlet/AdminServlet</url-pattern>
</servlet-mapping>
<!--
Cocoon handles all the URL space assigned to the webapp using its sitemap.
It is recommended to leave it unchanged. Under some circumstances though
(like integration with proprietary webapps or servlets) you might have
to change this parameter.
-->
<servlet-mapping>
<servlet-name>Cocoon2</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Finally, to use the XML:DB taglib included with eXist, you should define it as a built-in logicsheet in the configuration of Cocoon. Add the following to WEB-INF/cocoon.xconf:
Example: Adding a new built-in logicsheet to cocoon.xconf
<target-language name="java">
<!-- Defines the XSP Core logicsheet for the Java language -->
<parameter name="core-logicsheet" value="resource://org/apache/cocoon/components/language/markup/xsp/java/xsp.xsl"/>
<!-- Insert the following section: -->
<builtin-logicsheet>
<parameter name="prefix" value="xmldb"/>
<parameter name="uri" value="http://exist-db.org/xmldb/1.0"/>
<parameter name="href" value="resource://org/exist/xmldb.xsl"/>
</builtin-logicsheet>
<!-- ... more builtin-logicsheets ... -->
</target-language>
If you want to access eXist through Cocoon's XML:DB pseudo protocol, don't forget to add eXist's XML:DB driver to the <source-handler> section.The section should look something like this:
Example: Adding eXist's XML:DB driver
<source-handler logger="core.source-handler">
<!-- ...other predefined protocol handlers... -->
<!-- xmldb pseudo protocol -->
<protocol name="xmldb" class="org.apache.cocoon.components.source.XMLDBSourceFactory">
<!-- Xindice driver -->
<driver class="org.apache.xindice.client.xmldb.DatabaseImpl" type="xindice"/>
<!-- Add here other XML:DB compliant databases drivers -->
<driver class="org.exist.xmldb.DatabaseImpl" type="exist"/>
</protocol>
</source-handler>
You should now be able to restart your servlet-engine. You may check if eXist is running by browsing to http://your-host:8080/your-app/admin
ForrestProposal
Background
This is an evolving proposal to determine how to smoothly enable the Cocoon documentation to be built by Apache Forrest.
----
Procedure
Let us start by determining the dot-points in the "Outline" section below, to express requirements and issues.
- Please keep the dot-point numbers, just add new ones at the bottom. In this way we can just refer to item numbers during email discussion.
- Please add your comments or relevant links below any item.
- If you want to make major changes then please be sure to discuss that on
----
Outline of requirements and issues
1. Forrest to assist with building Cocoon system documentation. RELATED LINKS
- Using Forrest
- HowToForrestTransition
- Lots of discussion on cocoon-docs in late-March 2003
----
2. Forrest to automatically (or by manual trigger) generate the website at xml.apache.org/cocoon/
3. How will people who download just Cocoon, be able to locally build and work with its documentation? Do they need to install Forrest separately, or does Cocoon still build its own documentation using its own set of stylesheets?
PROPOSAL
- open
----
4. Cocoon xdocs need to be transformed to document-v11 DTD. Forrest has stylesheets, DTDs, anttasks, and XML validation to assist with this.
PROPOSAL
- Follow
HowToForrestTransition READER COMMENTS
- (ds) I believe I have successfully adapted earlier work to transform Cocoon docs to doc-v11 (and other) dtds.
- (ds) I have submitted a first draft. Issues remain. Please help resolve. See
----
5. After Cocoon xdocs are sucessfully transformed, the xdocs in CVS will be updated to be document-v11 type.
PROPOSAL
- One person needs to update CVS when they are happy with the transition, then others can help to refine it.
READER COMMENTS
- (ds) And do we delete legacy dtds or simply remove any configuration (schema) reference to them? (dc) Added new item 15 below to deal with this.
----
6. Simultaneously with #5, the Cocoon XSLT stylesheets, DTDs, and sitemaps will need to be updated to reflect the document-v11 from Forrest.
PROPOSAL
- open
READER COMMENTS
- (ds) How will we synchronize this with Forrest updates? (dc) ? Not sure what you mean Diana.
----
7. Expect a few people to help, especially during the transition of xdocs.
PROPOSAL
- At some stage we are going to need to just break the docs/webapp-docs parts of cocoon-2.1 and this disruption will take a few people to put it back together.
----
8. The documents that are published by Forrest need to be subsequently checked-in to the xml-site CVS.
DECISION
will do this. READER COMMENTS
- (ds) Do you mean the live site cvs? Why? I thought the whole point of Forrest automation was to remove this step?
- (dc) Yes because this is how all xml.apache.org sites need to currently do this.
- (dc) ToDo: See current
email discussionand gather the issues when it is finished.
- (bd) I think Forrestbot aims to automate this, see
- (jt) We have a bunch of Forrest sites updating every hour, visible at
http://forrestbot.cocoondev.org. Automatic or manually triggered update of xml-site/targets/* is possible, but not enabled by default. RELATED LINKS
- provides overview of the current cumbersome process to update the Cocoon website.
----
9. Use a consistent version of Forrest during the transition phase.
DECISION
- Forrest cvs head now has a cvs tag called "stable".
----
10. Decide which "tabs" to use. See
Apache Incubatorfor one example of tab use. DECISION
- Use the tabs that are suggested below. These are easy to re-arrange.
READER COMMENTS
- (bd) I suggest "Home, Usage, Reference, Wiki, Links". "Usage" would include FAQs, tutorials and How-Tos, there is certainly a better name than "usage" for this. "Wiki" would be just one page with info on the wiki role and a link to it.
----
11. Decide whether we want to show the "authors" of each document (which may not be relevant as it is a group effort).
PROPOSAL
- Do not show the author of the home page. Show authors of all other pages. Review the <author> element of all xdocs.
READER COMMENTS
- (dc) Authors should remain hidden, especially for general documents like the home page. Also many CVS mistakes have been made where a minor patch contributor is listed as an "author". Too easily abused. Also some pages still contain the author of another page which was used only as a template.
- (ds) This may be appropriate for core docs, but I think allowing authors to have their names on How-Tos will increase the incentive to contribute. See also
http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=102552672926362&w=2I personally like the idea of giving credit to those submitting revision patches. This helps users understand what has changed. See an example of this at: http://xml.apache.org/cocoon/howto/howto-paginator-transformer.html.
- (dc) I agree that contributors get recognition as shown with howto-paginator-transformer. However this is a different issue. The xml element document/header/author is what we are concerned about here.
- (dc) The author of any "group effort" page could be "cocoon-dev".
----
12. The status documents (changes, todo) need to merge into status.xml
13. Elevate doc-related changes and todo documents to top-level status file.
PROPOSAL
- open
READER COMMENTS
- (ds) See status-docs.xml in transition trial run sample files.
----
14. Need a mechanism to enable existing and future (with any reorganization) redirect pages.
PROPOSAL
- open
READER COMMENTS
- (dc) I would rather leave this until after transition.
----
15. Do the old documnet-v10 DTDs and OASIS Catalog entries need to remain in Cocoon CVS? Other people may be still using them for their own projects.
PROPOSAL
- Follow whatever Forrest decides to do.
READER COMMENTS
- (dc) How do we we mark them as deprecated?
- (dc) Or perhaps we just delete them and people need to use an old version of Cocoon if they need to still use the old document-v10 DTDs. This sounds better because if we keep the DTDs then that implies that we keep the stylesheets/sitemaps to go with them (yikes, management nightmare).
RELATED LINKS
- forrest-dev email:
----
16. Decide if Cocoon needs its own skin (to protect its identity/brand) before/shortly after transition, given the fact many sites are adopting the default skin and given the prospect of top-level project status.
PROPOSAL
- open
READER COMMENTS
- (dc) I would rather leave this until after transition.
----
17. Perhaps the navigation/organization of pages should be revisited at the same time.
PROPOSAL
- Leave this until after transition.
READER COMMENTS
- (dc) Perhaps this would make the job too big and so may delay it.
- (ds) Transition first and reorganize later!! First, we don't even have an efficient redirect mechanism in place. Second, transitioning will save me tons of time (which I could then devote to other doc concerns) as I won't have to perform manual updates to the live site.
----
18. Staging mechanism for quality control of website production
PROPOSAL
- Leave this issue until after transition is finished. Forrestbot already addresses part of this need.
RELATED LINKS
- cocoon-dev email:
- forrest-dev email:
RT: Site versioning, staging and deployment(Careful: There are followups with slightly different thread names.)
- cocoon-docs email:
----
19. From which branch does the website get generated - head or release branch?
PROPOSAL
- Open
READER COMMENTS
- (dc) It is currently generated from the release branch. Those xdocs are meant to be always in sync with the head of CVS. The documentation does describe the head of CVS and we do not maintain any separate "release" version of the docs (rather we put notes in the documentation to indicate which branch applies). So why do we not generate the website from the head of CVS? The only reason that i can think of is that the "changes.html" on the website describes the current release branch and not all relevant changes to head have gone into release branch, so the "changes" documents are necessarily different.
RELATED LINKS
- cocoon-docs email:
Out of which branch should the website be published?
----
Recent major changes
- 2003-04-06 Noted some decisions and fine-tuned the text.
----
Some relevant email discussion and documents
- which came to the decision to try this Wiki
- the vote was considered premature (need proposal first)
----
To Do
- Extract some more requirements and issues from
- Fine-tune and prioritise this list of issues, then develop the actual proposal (see
email).
----
See also
Abbreviated names of readers
For readability's sake:
- bd is
- dc is
- ds is
- jt is Jeff Turner
JBossDeployment
How to deploy Cocoon on JBoss and Tomcat
1. Deploy tomcat/jboss. Versions 4.0/2.4.3 are apparently known to work. We use 4.0.4 and 2.4.4. "Note that according to the Cocoon2 homepage certain beta versions of Tomcat does not work with Cocoon2." \\ \\ To deploy Tomcat 4.0.4 use the integrated 2.4.4/4.0.1 Tomcat/Jboss then copy a Tomcat 4.0.4 install over the "catalina" directory structure in the Jboss/Tomcat install. If you do this, I'd rename the base directory to help keep things straight as to what is installed. \\ \\ 2. Add environment variable CATALINA_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=7070.\\ This is to be able to debug java run in Tomcat. For Netbeans 3.2.1 for programming/debugging (remember to add port 7070 to debugging environment). \\ \\ 3. Delete in "[[your path]/JBoss-2.4.3_Tomcat-4.0/jboss/lib" following:
- crimson.jar
- jaxp.jar
- xml.jar
- (xml.jar is not present in latest versions of JBoss.)
\\ If Tomcat 3.x is used the following files must be also be deleted: from [[your path]/JBoss-xxx_Tomcat-3.x/tomcat/lib:
- parser.jar
- jaxp.jar
\\ \\ 4. new run.bat file in [[your path]/JBoss-2.4.3_Tomcat-4.0/jboss/bin to:
@echo off @if not "%ECHO%" == "" echo %ECHO% @if "%OS%" == "Windows_NT" setlocal set JBOSS_CLASSPATH=%JBOSS_CLASSPATH%;run.jar REM Add all login modules for JAAS-based security REM and all libraries that are used by them here REM need one of the two following lines for xerces support set JBOSS_CLASSPATH=%JBOSS_CLASSPATH%;../lib/xerces.jar REM set JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/xml-apis.jar REM Add the XML parser jars and set the JAXP factory names REM Crimson parser JAXP setup(default) REM set JBOSS_CLASSPATH=%JBOSS_CLASSPATH%;../lib/crimson.jar REM set JAXP=-Djavax.xml.parsers.DocumentBuilderFactory=org.apache.crimson.jaxp.DocumentBuilderFactoryImpl REM set JAXP=%JAXP% -Djavax.xml.parsers.SAXParserFactory=org.apache.crimson.jaxp.SAXParserFactoryImpl echo JBOSS_CLASSPATH=%JBOSS_CLASSPATH% java %JAXP% -classpath "%JBOSS_CLASSPATH%" org.jboss.Main %1 %2 %3 %4 %5 %6 %7 %8 %9 pause
5. If you want to be able to debug JBoss/Tomcat/Cocoon2 replace the last line (before pause) with:
java -Xint -Xdebug -Xnoagent -classpath "%JBOSS_CLASSPATH%" -Xrunjdwp:transport=dt_socket,server=y,address=12999,suspend=n org.jboss.Main %1 %2 %3 %4 %5 %6 %7 %8 %9
If you use Netbeans 3.2.1 for programming/debugging remember to add port 12999 to debugging environment.
6. copy xerces.jar to [[yourpath]/JBoss-2.4.3_Tomcat-4.0/jboss/lib. We used versions 1.4.3. Download xerces binary and use .jar file in the downloaded xerces_xxx.zip The Apache site suggests that you should copy xml-apis.jar from cocoon/lib/core/ to jboss/lib. However, I did not have to do this. If you think you need xml-apis. jar you may also want to uncomment the line in run.bat that refers to it and comment the line that uses xerces. Only one of these should be needed...
7. add environment variable TOMCAT_HOME=[[yourpath]/JBoss-2.4.3_Tomcat-4.0/catalina/ (if a Tomcat 3.x version is used "catalina" must be substituted with "tomcat"). This is for use with Cocoon ant file: build.bat.
8. Test that JBoss/Tomcat starts up and responds on port 8080/jboss Contrary to some documentation the test application still works fine.
Cocoon and Java 1.4 configuration
Cocoon requires more recent versions of the Xerces and Xalan libraries than those shipped with j2se 1.4. To override bundled libraries, follow these steps:
1. Create %JAVA_HOME%\jre\lib\endorsed directory.
2. Copy xerces-XXX.jar, xalan-XXX.jar, and the xml-apis.jar from the .\lib\core\ to the %JAVA_HOME%\jre\lib\endorsed\ directory.
Due to changes in JDBC between JDK 1.3 and JDK 1.4, it is not possible to use Cocoon built on JDK 1.3 with JDK 1.4 when it comes to database connections. Make sure you have a 1.4 compatible Cocoon build, or if you have the Cocoon source build it yourself with 1.4.
How to change Tomcat's Server port from 8080 to 80 using the JBoss-2.4.4-Tomcat-4.0.1 integrated package
JBoss will NOT read the server.xml of Catalina (tomcat4).
To change port, edit $JBOSSHOME/conf/Catalina/jboss.jcml and look for the mbean configuration:
<mbean code="org.jboss.web.catalina.EmbeddedCatalinaServiceSX" name="DefaultDomain:service=EmbeddedTomcat"/>
then change it as follows:
<mbean code="org.jboss.web.catalina.EmbeddedCatalinaServiceSX" name="DefaultDomain:service=EmbeddedTomcat">
<attribute name="Port">80</attribute>
</mbean>
restart JBoss (with Tomcat/Catalina).
Instructions for Oracle 8.1.7 and JBoss 2.4.0
warning: see http://marc.theaimsgroup.com/?l=xml-cocoon-users&m=103581609814762&w=2
Using these instructions, you can use CMP beans or connect to Oracle via:
InitialContext context = new InitialContext();
DataSource ds = (DataSource) context.lookup("java:/ctdb");
1. Copy oracle's classes12.zip (this is available from Oracle technet,and has the jdbc driver) to $JBOSS_DIST/lib/ext.
2. Note that jboss.properties does NOT need to be modified (as indicated in the jboss.properties file).
3. Modify $JBOSS_DIST/conf/tomcat/jboss.jcml: Uncomment the XidClassName attribute shown below:
<!-- ==================================================================== -->
<!-- Transactions -->
<!-- ==================================================================== -->
<mbean code="org.jboss.tm.TransactionManagerService" name="DefaultDomain:service=TransactionManager">
<attribute name="TransactionTimeout">300</attribute>
<!-- Use this attribute if you need to use a specific Xid implementation -->
<attribute name="XidClassName">oracle.jdbc.xa.OracleXid</attribute>
</mbean>
4. Also in jboss.jcml: add the Oracle driver to the JDBC drivers (this driver is in classes12.zip):
<!-- ==================================================================== -->
<!-- JDBC -->
<!-- ==================================================================== -->
<mbean code="org.jboss.jdbc.JdbcProvider" name="DefaultDomain:service=JdbcProvider">
<attribute name="Drivers">org.hsql.jdbcDriver,org.enhydra.instantdb.jdbc.idbDriver,oracle.jdbc.driver.OracleDriver</attribute>
</mbean>
5. Also in jboss.jcml in the JDBC section, define what the oracle datasource name is (say OracleDB), and the connection URL. In the code below, replace jdbc:oracle:thin:@<host>:<port>:<sid>with the appropriate string for your installation. Such as jdbc:oracle:thin:@192.168.1.111:1521:testdb
<mbean code="org.jboss.jdbc.XADataSourceLoader" name="DefaultDomain:service=XADataSource,name=datasourcename">
<attribute name="PoolName">DefaultDS</attribute>
<attribute name="DataSourceClass">org.jboss.pool.jdbc.xa.wrapper.XADataSourceImpl</a
ttribute>
<attribute name="URL">jdbc:oracle:thin:@<host>:<port>:<sid></attribute>
<attribute name="JDBCUser">username</attribute>
<!-- set correct username here -->
<attribute name="Password">password</attribute>
<!-- set correct password here -->
</mbean>
or alternatively, this may be necessary:
<mbean code="org.jboss.jdbc.XADataSourceLoader" name="DefaultDomain:service=XADataSource,name=datasourcename">
<attribute name="PoolName">poolname</attribute>
<!-- as needed -->
<attribute name="DataSourceClass">org.jboss.pool.jdbc.xa.wrapper.XADataSourceImpl</attribute>
<attribute name="Properties"></attribute>
<attribute name="URL">jdbc:oracle:thin:@sjmemctd2:1521:ctdev2</attribute>
<attribute name="GCMinIdleTime">1200000</attribute>
<attribute name="JDBCUser">username</attribute>
<!-- set correct user name here -->
<attribute name="MaxSize">10</attribute>
<attribute name="Password">password</attribute
<!-- set correct password here -->
<attribute name="GCEnabled">false</attribute>
<attribute name="InvalidateOnError">true</attribute>
<attribute name="TimestampUsed">false</attribute>
<attribute name="Blocking">true</attribute
<!-- may cause exception if set false -->
<attribute name="GCInterval">120000</attribute>
<attribute name="IdleTimeout">1800000</attribute>
<attribute name="IdleTimeoutEnabled">false</attribute>
<attribute name="LoggingEnabled">true</attribute>
<attribute name="MaxIdleTimeoutPercent">1.0</attribute>
<attribute name="MinSize">0</attribute>
</mbean>
6. Modify $JBOSS_DIST/conf/tomcat/standardjaws.xml to use the proper datasource via the datasourceelement, and to use Oracle8 jdbc-sql type mappings. (This is for container managed persistance.) Make sure the datasource name matches whatever you defined for the datasource name in step 5.
<jaws>
<datasource>java:/datasourcename</datasource
.
.
.
---- Authors: \\ written by Peter Hunsberger original mail\\ added to the docs by Reinhard Pötzmail
Jars2exclude
I wrote 2 XSLT- files that are working fine to exclude jars from lib/*, but I would like to have more "intelligence" (please see below). So consider this a work in progress -- ----
Background
I created a minimal webapp and found the following remark from Geoff Howard in [1] CreateMinimalWebapp - you have to read this reference referenceto understand the context of what we are going to do:\\ Geoff Howard wrote:\\
- 2. edit lib/jars.xml to comment out/remove all the optional jars except the ones noted above\\
- 1. could be done with an xsl transform\\
Goal
You will learn how to write a xsl transform that will remove certain XML-elements. This transform is capable of exceptions.\\
Introduction
There are 2 Steps to do. This steps are achieved by 2 XSLT- files.\\
- Step 1 Label lib which should be excluded with "2exclude", leave exceptions untouched.
- Step 1a Label lib which should be excluded with "2exclude"\\
- Step 1b Leave exceptions untouched\\
- Step 2 Copy all elements except those which are labeled "2exclude"\\
sitemap.snippet:
<map:pipeline> <!-- Step 1: Label lib which should be excluded with "2exclude", leave exceptions untouched. --> <map:match pattern="2Xclude.xml"> <map:generate src="xml/jars.xml"/> <map:transform src="xsl/jars2exclude.xsl"/> <map:serialize type="xml"/> </map:match> <!-- Step 2: Copy all elements except those which are labeled "2exclude"--> <map:match pattern="exclude"> <map:generate src="cocoon:/2Xclude.xml"/> <map:transform src="xsl/exclude2jars.xsl"/> <map:serialize type="xml"/> </map:match> </map:pipeline>
Step 1a: Label lib which should be excluded with "2exclude"
The first xslt (jars2exclude.xsl [2] ) checks the <lib> element form the jars.xml whether it starts with 'optional/' (the dir to exclude files from). If so, they will be labeled "2exclude" (there are exceptions - you will see later).\\ The code looks like this:
<xsl:template match="lib"> <lib> <xsl:choose> <!--==Excluding dir ==--> <xsl:when test="starts-with(text(), 'optional/')"> 2exclude </xsl:when> <!--/==Excluding dir ==--> <xsl:otherwise> <xsl:value-of select="."/> </xsl:otherwise> </xsl:choose> </lib> </xsl:template>
The jars.xml looks like e.g.:\\
... <file> <title>The XML parser</title> <description>Xerces is an XML parser.</description> <used-by>Cocoon</used-by> <lib>core/xercesImpl-2.0.0.jar</lib> <homepage>http://xml.apache.org/xerces-j/</homepage> </file> ...
The example file would not meet the criteria (starts-with(text(), 'optional/')). The following would:
<file> <title>Servlet API</title> <description/> <used-by>Cocoon</used-by> <lib>optional/servlet_2_2.jar</lib> <homepage>http://jakarta.apache.org/tomcat/</homepage> </file>
But this certain file has to be left untouched [1] - this is one of n exceptions (2 are mentioned in [1] , but I need more<-n exception). This will now be explained.
Step 1b Leave exceptions untouched
We introduced one <xsl:choose>. If we nested a little bit deeper we can define exceptions. An exception looks like this:
<xsl:when test="substring-after(text(), 'optional/')='servlet_2_2.jar'"> <xsl:value-of select="."/> </xsl:when>
You can define as many exceptions as you want:
<lib>
<xsl:choose>
<!--==Excluding dir ==-->
<xsl:when test="starts-with(text(), 'optional/')">
<xsl:choose>
<!--==without the exceptions (as many as you want just add a <xsl:when/>==-->
<xsl:when test="substring-after(text(), 'optional/')='fop-0.20.4.jar'">
<xsl:value-of select="."/>
</xsl:when>
<xsl:when test="substring-after(text(), 'optional/')='servlet_2_2.jar'">
<xsl:value-of select="."/>
</xsl:when>
<xsl:when test="substring-after(text(), 'optional/')='commons-jxpath-1.0.jar'">
<xsl:value-of select="."/>
</xsl:when>
<!--/==without the exceptions==-->
<xsl:otherwise>2exclude</xsl:otherwise>
</xsl:choose>
</xsl:when>
<!--/==Excluding dir ==-->
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</lib>
The result of the transformation [2] will look like this:
<file> <title>XML Catalog Entity Resolver</title> ... <lib>2exclude</lib> ... </file> <file> <title>Servlet API</title> ... <lib>optional/servlet_2_2.jar</lib> ... </file>
Step 2: Copy all elements except those which are labeled "2exclude"
Transforming this result with exclude2jars.xsl [3] will result in the new jar.xml (without the exclusions, but with the exceptions). We just have to introduce another XPath function\\
<xsl:template match="jars"> <jars> <xsl:copy-of select="file[lib!='2exclude']"/> </jars> </xsl:template>
\\
Help wanted!
Ok, that is nice but I would prefer not to hard code the exceptions, e.g.:\\ substring-after(text(), '__optional/')='__servlet_2_2.jar'\\ hard coded exception e.g. optional/\\ \\ I prefer to use something like jars2exclude.xml [4] where I declare the hard coded exceptions. \\
<jars2exclude> <exclude> <dir2exclude>optional/</dir2exclude> <except>fop-0.20.4.jar</except> <except>commons-jxpath-1.0.jar</except> <except>servlet_2_2.jar</except> </exclude> </jars2exclude>
\\ It would be nice as well to actually delete the files in the lib/*.\\ ..but how can I do that?\\
Any help and any ideas welcome.
King regards\\ Thorsten Scherler
Reference
http://wiki.cocoondev.org/Wiki.jsp?page=CreateMinimalWebapp\\ jars2exclude.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="jars">
<jars>
<xsl:apply-templates select="file"/>
</jars>
</xsl:template>
<xsl:template match="file">
<file>
<xsl:apply-templates/>
</file>
</xsl:template>
<!--================Parsing of value===============================-->
<xsl:template match="title">
<title>
<xsl:value-of select="."/>
</title>
</xsl:template>
<xsl:template match="description">
<description>
<xsl:value-of select="."/>
</description>
</xsl:template>
<xsl:template match="used-by">
<used-by>
<xsl:value-of select="."/>
</used-by>
</xsl:template>
<!--==Excluding dir without the exceptions==-->
<xsl:template match="lib">
<lib>
<xsl:choose>
<!--==Excluding dir ==-->
<xsl:when test="starts-with(text(), 'optional/')">
<xsl:choose>
<!--==without the exceptions==-->
<xsl:when test="substring-after(text(), 'optional/')='fop-0.20.4.jar'">
<xsl:value-of select="."/>
</xsl:when>
<xsl:when test="substring-after(text(), 'optional/')='servlet_2_2.jar'">
<xsl:value-of select="."/>
</xsl:when>
<xsl:when test="substring-after(text(), 'optional/')='commons-jxpath-1.0.jar'">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>2exclude</xsl:otherwise>
<!--/==without the exceptions==-->
</xsl:choose>
</xsl:when>
<!--/==Excluding dir ==-->
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</lib>
</xsl:template>
<xsl:template match="homepage">
<homepage>
<xsl:value-of select="."/>
</homepage>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <xsl:apply-templates/> </xsl:template> <xsl:template match="jars"> <xsl:comment> Add an entry for each jar file used by Cocoon, following the other entries Author: Ovidiu Predescu "ovidiu@cup.hp.com" Date: May 23, 2002 jars2exclude have been used. Author: Thorsten Scherler "thorsten.scherler@wyona.org" Date: March 06, 2003 This is the modified jars.xml. </xsl:comment> <jars> <xsl:copy-of select="file[lib!='2exclude']"/> </jars> </xsl:template> </xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?> <!-- Add an entry for each jar file which should be excluded by Cocoon! Author: Thorsten Scherler "thorsten.scherler@wyona.org" Date: March 05, 2003 --> <jars2exclude> <exclude> <dir2exclude>optional/</dir2exclude> <except>fop-0.20.4.jar</except> <except>commons-jxpath-1.0.jar</except> <except>servlet_2_2.jar</except> </exclude> </jars2exclude>
<map:pipeline> <!-- Step 1: Label lib which should be excluded with "2exclude", leave exceptions untouched. --> <map:match pattern="2Xclude.xml"> <map:generate src="xml/jars.xml"/> <map:transform src="xsl/jars2exclude.xsl"/> <map:serialize type="xml"/> </map:match> <!-- Step 2: Copy all elements except those which are labeled "2exclude"--> <map:match pattern="exclude"> <map:generate src="cocoon:/2Xclude.xml"/> <map:transform src="xsl/exclude2jars.xsl"/> <map:serialize type="xml"/> </map:match> </map:pipeline>
ModularDatabaseActions
The Modular Database Actionsprovide similar funcionality as Original Database Actions. Were mainly created to make avaliable the support for autoincrement columns, handle input and output flexibily and have a consistent interface.
A sucessful action will return the number of affected rows into a sitemap parameter called row-count
You can also use Form Validation Using Cocoonbefore send the data to the database
----
Main improvements over the
- Allow to store all the database structure in a single file
- Flexible input and output using modules.
- Support for autoincrement column type that cover a wide range of database systems.
----
Usage
Describing the Structure of your DB - descriptor.xml
All the metadata are stored in a XML file, that can contain the description of any number of tables. The tag table-setdefine the current used set of tables. Into this file we can include description rules for others Actions like Form Validator Action. IN this way we can have all the database metadata in the same file.
Example of a descriptor file:
<root>
<connection>personnel</connection>
<table name="user" alias="user">
<keys>
<key name="uid" type="int" autoincrement="true">
<mode name="auto" type="autoincr"/>
</key>
</keys>
<values>
<value name="name" type="string"></value>
<value name="firstname" type="string"></value>
<value name="uname" type="string"></value>
</values>
</table>
The
aliasattribute of tableelement The optional attribute aliasin the tableelement, is used to create an alternative name of the table, wich can be used inside a table-set. This tag can be used if a complex join expresions is used as table name.
Another usage is when different number of columns will be affected by different operations or if a table contains several candidate keys that are used alternatively. This way we can create "different views" of the same table
To find a table, the descriptor file is searched top-down for tables whose nameor aliasmatch.
Key Columns
The descriptor file resembles the one for the Original Database Actions. Note the absence of dbcoland paramattributes.
The new nameattribute specifies the database column name and corresponds to the dbcolattribute.
The autoincrementattribute indicate if a column is of this type. Auto increment columns will be handled differently on insert operations.
Instead of specifying a parameter name, the actions support to use different input modules for each operation through the nested modeelements.
No every column needs a modeelement. The actions default module is defined as requestin the default installation to obtain the values from request parameters. The implicit name of the parameter is table_name.column_name.
Other Columns
Everything said above applies to value columns as well. Except the autoincrementattribute.
Operation Mode Types
Basically, two different mode types exist: autoincrementwhich is used only on INSERT when a key has set this attribute and othersfor all other requirements.
In addition, a table-set can specify different mode types to use instead of the predefined type names. Through this, and the fact that every mode can specify a different input module, it is easy to use different input modules for different tasks and forms.
One special mode type name exists that matches all requested ones: all. This makes it easier to configure only some columns differently for each table-set.
How to obtain Values
As said above, the actions default to reading from request parameters with a default parameter name. This can be overridden using modeelements. Any component that implements the InputModule interface can be used to obtain values. How to make such modules known to Apache Cocoon is described in http://xml.apache.org/cocoon/userdocs/concepts/modules.html.
Beside using different input modules, their parameters can be set in place, for example to override parameter names, configure a random generator or a message digest algorithm.
<table name="user_groups">
<keys>
<key name="uid" type="int">
<mode name="request" parameter="user_groups.uid" type="request"/>
<mode name="attribute"
parameter="org.apache.cocoon.components.modules.output.OutputModule:user.uid[0]"
type="attrib"/>
</key>
<key name="gid" type="int" set="master">
<mode name="request" parameter="user_groups.gid" type="all"/>
</key>
</keys>
</table>
The above example shows that: the parameterattribute is not read by the database action itself but the complete mode configuration object is passed to the input module. Both the request attribute and the request parameter input modules understand this parameter attribute which takes precedence over the default one.
Another feature when obtaining values is tied to the typeattribute: Different modes can be used in different situations. The basic setup uses two different mode types:
- autoincrement
when inserting in key columns that have an indicator that they are indeed auto increment columns and
- others
for insert operations on all other columns and all other operations on all columns.
Table-sets can override the default names for these two mode type name categories with arbitrary names except the special name all, wich is used with all requested type names. Lookup obeys first match principle so that all modes are tested from top to bottom and the first that matches is used.
How to store Values e.g. in your Session
All modular database action can be configured to use any component that implements the OutputModuleinterface to store values. The output module is chosen on declaring the action in the sitemap or dynamically with a sitemap parameter. If no output module is specified, the default it to use the request attribute module. ----
Note
The interface does not allow to pass configuration information to the output module. This has to be done when the module is declared e.g. in cocoon.xconf. ----
Inserting Multiple Rows - Sets
A common task need to work on more than one row. If the rows are in different tables, this is catered for by table-sets. Operating on multiple rows of one table requires to mark columns that should vary and among those one, that determines the number of rows to work on.
This is done with sets. All columns that cary a setattribute can vary, those, that don't, are kept fixed during the operation. The column that is used to determine the number of rows is required to have a value of masterwhile all others need to have a value of slavefor the set attribute. There may be only one master in a set.
Sets can be tagged either on column or on mode level but not both for a single column.
Select Your Tables - Table-Sets
Tables that should be used during an operation can be grouped together with a table-set. A table-set references tables by their name or their alias.
Also, a table-set can override the mode type names for the two categories autoincrementand others.
Operations spanning multiple tables in a table-set are done in a single transaction. Thus, if one fails, the other is rolled back.
<table name="groups">
<keys>
<key name="gid" type="int" autoincrement="true">
<mode name="auto" type="autoincr"/>
</key>
</keys>
<values>
<value name="gname" type="string"/>
</values>
</table>
<table-set name="user">
<table name="user"/>
</table-set>
<table-set name="groups">
<table name="groups"/>
</table-set>
<table-set name="user+groups">
<table name="user"/>
<table name="user_groups" others-mode="attrib"/>
</table-set>
<table-set name="user_groups">
<table name="user_groups" others-mode="request"/>
</table-set>
</root>
OlderNews
Older News
- January 22
- Added
XMLFormXindice, a how-to on using Xindice for storage of XML resources with data gathered using XMLForms. JosemaAlonso
- January 20
- Added
FileUploadsWithCocoon, an overview of how Cocoon deals with multipart file uploads and the configuration options available. GeoffHoward
- January 9
- Added
BestPractices, also in the left menu (also added OurWishlistsso that the menu is same size as before) -- BertrandDelacretaz
- January 8
- Added
Flow. 'Nuff said. -- TonyCollen
- Tweaked
Matchersafter some traffic on cocoon-dev about escaping '{' characters with the regexp matcher. -- TonyCollen
- January 3
- Added
- December 27-28
- Transition to
http://wiki.cocoondev.orgmostly completed - now running JSPWiki v2.0.7 (attachments! visual external hyperlinks! page templates! delete revisions!) - and implemented rough Forrest skin -- Steven Noels
- December 18
- Updated
DocbookTransformation:Using Docbook and the stylesheet by Norman Walsh in Cocoon -- Gabridome
- December 14
- Cleaned up
Unused Pagesand Dead Pages, someone volunteering to take a look at Undefined Pageswould be nice -- Steven Noels
- December 4
- Added
- Added
MySQLBoth on SpecificDatabaseConnections-- Gabridome
- December 2
- Added
WebServiceProxyGenerator-- TonyCollen
- Added RequestParameterSelector --
ArjeCahn-- changed to WildcardRequestParameterMatcher, thanks Con
Oops.. (December 9)
- November 30
- Added
- November 20 (Ghent GetTogether +1 - start of a new era;-)
- Added
HowToForrestTransition-- Diana Shannon
- Added
ProductsBuiltForCocoonand ProductsBuiltOnCocoonto separate this from Links-- Bertrand Delacretaz
- November 18 (Ghent GetTogether -1)
- Added
CocoonDocsPlan- this is being discussed on cocoon-docs -- Bertrand Delacretaz
- November 16
- Added
Cocoon2.1DevSetupWin2000- Cocoon 2.1 Dev instal for Dummies on Windows 2000 - Franck Lumpe
- November 15
- Added
DeadPages- come on kids, go play in the sand box... -- Bertrand Delacretaz
- November 11
- Added
PHPGeneratorand started writing a verbose installation HOWTO. -- Tony Collen
- Updated
As400section in SpecificDatabaseConnection, adding some notes on JDK switching and definition of JNDI connection pools -- Lorenzo De Sio
- November 8
- Added
DevelopingComponentsand moved part of ImplementingTransformersto WritingPipelineComponents. -- Bruno Dumon
- November 7
- Added
ImplementingTransformers- Introduction on how to implement transformers BrunoDumon
- Added
ExploringTheLogs- How to make sense of the Cocoon logs? -- Bertrand Delacretaz
- Added
SpecificDatabaseConnection- How can I connect cocoon to my xxx database? -- gabridome
- November 4
- Added
GraphicalWikiDream- who would like to drawon the wiki pages? -- Bertrand Delacretaz
- November 3
- Added
ForrestProposalto determine how to automate Cocoon documentation rendering -- DavidCrossley
- November 2nd
- Added
ImageReaderexplaining how to use ImageReader to generate image thumbnails -- Litrik
- Added
BlocksDefinition-- Written by Stefano Mazzocchi, wikified by Steven Noelsand Bertrand Delacretaz
- 1st November
- Added
- 17th October
- Added
CocoonDocsDrafts-- Bertrand Delacretaz
- 14th October
- Added
DocumentationTracksProject-- Bertrand Delacretaz
- 9th October
- Added
AboutTheBuildTargets-- Tony Collen
- 1st October
- Cleaned up some typos and incorrect spelling in
DocbookTransformation-- Tony Collen
- 20th September
- Added
HtmlTableCellNonBlanking-- Alan Hodgkinson
- 13th September
- Added
HowToCreateNewPages-- Bertrand Delacretaz
- Added
- 6th September
- Updated the
SAXand pointed at it from the FAQ- Ola Berg
- 3rd September
- Added an
Eventslisting and a first event -- Steven Noels
- 1st September
'Back to School' ** Contributors or users wanting to be
- 30th August
- Reshuffled
SimilarServerspage -- Steven Noels
- 23rd August
- Added
Random Thoughton Separation of Logic and Contentfrom Alan Hodgkinson(after asking for permission) -- Jens Lorenz
- 22nd August
- Added How To
Integrate A Servletwhich is a summary of a brief discussion on cocoon-users. It'd be useful to mine other similar tidbits from the mailing lists. -- Leigh Dodds
- 18th August
- Added a request for
Actions in XSP Documentationto the Wishlist
- 16th August
- Added
Sourceand SourceVsGenerator. Work in progress ! -- Sylvain Wallez
- Added
BlocksUseCases-- Bertrand Delacretaz
- 12th August
- Added
OutputEncoding-- Bertrand Delacretaz
- 9th August
- Defined
AddCIncludeToMinimalSitemap-- Andrew C. Oliver
- Added new How-To about
performing simple XSLT transformations-- Leigh Dodds
- Added
CocoonProtocolExample-- Bertrand Delacretaz
- 8th August
- Added mod_xslt to
- Added
CIncludetransformer information and examples.
- Minor changes here and there regarding
- Warning that the
XIncludetransformer is NOT up to the specification, and kind of some *why* on the CIncludetransformer. -- Andrew C. Oliver
- Added
SimilarServers-- Bertrand Delacretaz
- Added
Serving Static Filesto How-Tos, and more documentation about the Readercomponent. -- Leigh Dodds
- Added
Error Handlingpage -- Leigh Dodds
- 7th August
- Added
PossibleApplicationswhere people can throw out ideas for unique applications of Cocoon -- Tony Collen
- Added a place-holder for a Cocoon
Roadmap. Anyone care to start adding descriptions? -- Leigh Dodds
- Added a RT about
Distributing Cocoon Applications-- Leigh Dodds
- Added
XLSSerializerto the MinimalSitemapConfigurationpage which builds on the example and shows how to serialize to Excel based upon the example. Andrew C. Oliver
- Added
DoYouLikeCocoon. While the Wishlistis about this Wiki, the idea for DoYouLikeCocoonis to get short "I like/I don't like" comments about Cocoon itself -- Bertrand Delacretaz
- Fixed up some broken references in the notes I uploaded, some formatting tweaks to various pages. Added the 'Referenced By' section to the left menu --
- Added a
Licensepage to make things clear on a legal ownership level -- Steven Noels
- Added diagram to
XSP Fundamentals, added How-Toon Meta Stylesheets-- Leigh Dodds
- 6th August -- Poured in a chunk of content from my personal Wiki. This constitutes a collection of notes I compiled whilst writing three tutorials on Cocoon for IBM developerWorks. They're supplemented with some recent notes I've gathered on
UnderstandingCocoonMountsand Resources. Any mistakes are entirely mine! The notes have been collected from all the Cocoon 2.0.x series, so there may be some inconsistencies where functionality has changed -- Leigh Dodds
- August -- Site created by
OpenOfficeGeneration
HOW TO USE OPENOFFICE FILES IN A GENERATOR IN COCOON.
Author: Yves Vindevogel \\Date: 2003-03-14 ---- Hi all,
I think I got a solution to use OpenOffice (Writer) files as a generator in Cocoon.
First of all, thanks to Conal Tuohyand Upayavirafor pointing me at some very important details.
Okay, what I did ....
1. files and folders
My cocoon folder looks like this: \\cocoon/resources/entities
The sitemap for the Implements site is mounted as a submap of the sitemap in the /cocoon directory.
I've got the file "test.sxw" in my directory /implements/sxw \\I've got the file "html.oowriter.xsl" in my directory /implements/xsl \\I've got the files from, in my case opt/OpenOffice.org1.0/share/dtd/officedocument/1_0 \\(the DTDs from OpenOffice) copied to the folder /cocoon/resources/entities
2. Sitemap
This is my sitemap (well, the part for OO)
\\<map:match pattern="sxw/*.html">
The basic element is this one is the "jar:http://" thing. Thanks to Conal for pointing me to this. OpenOffices uses a kind of zip file to store its files in. The Jar: protocol is able to read those zips straight away. Conal made a wiki page for this: \\JarProtocolExample
The only problem is the need for a "http://web/....". \\You need to specify the full path, the Jar: protocol does not work with the cocoon sitemap.
His wiki page shows the <map:read>, which works fine. You can read whatever you want from the zip file.
3. Generator
I went further: I wanted to use the content.xml in that zip in a generator. \\So I changed the <map:read> into <map:generate> \\This results in an error:
\\message File "jar:http://web/implements/test.sxw!/office.dtd" not found.
This is correct, the dtd is not in the zipped file. It's only a reference.
4. Fixing problem 1: modify the catalog
I modified the /resources/entities/catalog file. I added two lines \\(Thanks Upayavira for showing me this thing)
\\-- Open Office DTDs --
That corrects the error, but gives a new one ....
\\org.apache.cocoon.ProcessingException:
5. Fixing problem 2: modify the DTD
Appearantly, there's something in that file. I looked at it, but I could not find a mistake. I'm not an expert on those things, so, maybe someone can look into it for me ....
I used a workaround however. You can modify the /resources/entities/office.dtd (from OO) file. There's entries like this:
\\<!ENTITY % dtypes-mod SYSTEM "dtypes.mod">
When you remove them all, the generator should work (Sax does not check a lot, this is a workaround, not a solution ;--)))
6. Aggregation
Next thing I did, was to use the <map:aggregate> to combine the files from the zipped file into one. I enclose the original xml from all the files in a new <document> tag. At first, I used the <office:document> tag, but this gave problems in my XSL.
After that, it outputs both files as a combined xml file, like we needed !
7. Sample XSL
I then reused an xsl file I wrote before, when I extracted the files to xml with a little perl too. This xsl file generates a very simple page in html, based on the document ...
\\<?xml version="1.0" encoding="UTF-8"?>
Et voila !!!! That's it .... \\Once again, thanks to all who helped.
Could somebody please check this to see if he/she could reproduce my work on his/her machine ??
Yves Vindevogel
Implements \\Mail: yves.vindevogel@implements.be-- http://www.implements.be
Quote: The winner never says participating is more important than winning.
RhinoWithContinuations
Transcript of
Flow scripts in Cocoon are written in JavaScript. Why JavaScript ? Because it's a high-level scripting language that looks a lot like Java, and also because we have a special version of the Rhino JavaScript interpreter that has the ability to "capture" the current execution state of a program.
This special version is hosted on Cocoondev.org.
The org.mozilla.javascript.continuationspackage introduces an additional interpreted mode for Rhino that supports tail-call elimination and first-class continuations. Currently this mode is selected by setting the optimization level of the current context to -2. It may also be selected by passing "-opt -2" on the command line to the Rhino shell or debugger, for example like this:
(shell:)
% java -cp js.jar org.mozilla.javascript.tools.shell.Main -opt -2 file.js
(debugger:)
% java -cp js.jar org.mozilla.javascript.tools.debugger.Main -opt -2 file.js
Features
Tail-call elimination
You might think the following code is faulty since it apparently would overflow the call-stack given a large enough value for 'limit':
function g(count, limit) {
if (count == limit) {
return "done";
}
return g(count + 1, limit);
}
In fact, with the "continuations" mode of Rhino this is not the case. The interpreter detects that the recursive call to 'g()' is in so-called "tail position" (meaning that there is no further code to execute after the call) and simply overwrites the current call frame with the new call to 'g()'. Thus no additional stack space is used in such cases.
Continuations
Rhino now supports first-class continuations. In Rhino a continuation is a JavaScript object that represents a snapshot of the state of an executing Rhino script -- i.e the current call-stack -- including each call-frame's program counter and local variables. The term "Continuation" (borrowed from Scheme) is used because it represents the rest of, or the "continuation" of, a program. Each time you call a function, there is an implicit continuation -- the place where the function should return to. A JavaScript Continuation object provides a way to bind that implicit continuation to a name and keep hold of it. If you don't do anything with the named continuation, the program will eventually invoke it anyway when the function returns and passes control to its caller. Now that it's named, however, you could invoke the continuation earlier than normal, or you could invoke it later (after it has already been invoked by the normal control flow). In the early case, the effect is a non-local exit. In the later case, it's more like returning from the same function more than once.
You can capture the continuation of a script by simply calling
new Continuation()
for example:
function someFunction(a, b) {
var kont = new Continuation();
}
The variable kontnow represents the execution state of the current caller of someFunction. (For those of you who are familiar with Scheme's call-with-current-continuation, here is the Rhino equivalent:
function call_with_current_continuation(fun) {
var kont = new Continuation();
return fun(kont);
}
Since kontis a first-class JavaScript object you can return it, store it in a variable, assign it to a property of another object - whatever you like. In addition, a Continuation object is also a function, which may be called. When you make such a call the current execution state of the program is discarded and the snapshot of the program represented by the Continuation object is resumed in its place. You may also pass an argument to the call to a Continuation object (if you don't pass an argument 'undefined' is implicitly passed instead). The value you pass as an argument to the Continuation object becomes the return value of the function in which the Continuation was captured. For example:
01 function someFunction() {
02 var kont = new Continuation();
03 print("captured: " + kont);
04 return kont;
05 }
06
07 var k = someFunction();
08 if (k instanceof Continuation) {
09 print("k is a continuation");
10 k(200);
11 } else {
12 print("k is now a " + typeof(k));
13 }
14 print(k);
Evaluating the above script yields the following output:
captured: [object Continuation] k is a continuation k is now a number 200
When the continuation kis invoked on line 10, the program "jumps" back to the call to someFunctionon line 7, but this time someFunctionreturns with the value '200' (which was passed into the call to kon line 10).
In addition, a Continuation object may be called more than once. Each time it is called it restarts execution at the return point of the function in which it was captured. This means that the same function invocation can return multiple times (and with different return values).
Finally, note that a Continuation created in a top-level script provides a means to terminate any script immediately. Whenever such a Continuation is invoked it simply terminates the interpreter, for example:
var suicide = new Continuation();
function foo(suicide) {
print("commiting suicide");
suicide();
print("never reached");
}
foo(suicide);
ContinuationException
A Continuation can be thought of as representing the return from a function invocation. In JavaScript, in addition to a normal return, a function invocation may return due to an exception. In continuations mode, Rhino provides a special built-in object ContinuationExceptionwhich allows you to throw an exception to a Continuation. ContinuationException's constructor takes one argument - the object you want to throw. When an instance of ContinuationException is passed to a Continuation, the value of that argument is thrown in the context of the Continuation after the Continuation is restored. For example:
function someFunction() {
var k = new Continuation();
return k;
}
try {
var k = someFunction();
print("k: " + k);
if (k instanceof Continuation) {
print("k is a continuation");
k(new ContinuationException("this is thrown from someFunction"));
}
print("never reached");
} catch (e) {
print("caught exception: " + e);
}
Evaluating the above script yields the following output:
k: [object Continuation] k is a continuation caught exception: this is thrown from someFunction
Controlling what gets captured in a Continuation
In continuations mode, Rhino provides a special extended syntax to allow you to control what gets captured in a continuation as follows:
catch (break) {
// a continuation has been captured - code to handle that
// goes here
}
catch (continue) {
// a continuation has been resumed - code to handle that
// goes here
}
Multiple such "catch" clauses may be present at any scope. All such clauses contained within a script or function invocation captured or resumed as part of a Continuation will be executed when that Continuation is captured or resumed. For example, you might want to return a pooled JDBC connection to its connection pool while a Continuation is suspended and recover the connection when the Continuation is resumed:
var pool = ...;
function someFunction() {
var conn = pool.getConnection();
...
catch (break) {
conn.close();
conn = null;
}
catch (continue) {
conn = pool.getConnection();
}
}
Continuations are Serializable
This version of Rhino also contains experimental support for serializing Continuations. Thus you may save the state of an executing script and restore it later. Here is an example:
function capture(filename) {
var k = new Continuation();
serialize(k, filename);
java.lang.System.exit(0);
}
function foo(level) {
var now = new java.util.Date();
if(level > 5) {
print("run the file foo.ser");
capture("foo.ser");
} else {
print("next level");
foo(level + 1);
}
print("restarted("+level+"): " + now)
}
foo(1);
Evaluating the above script saves a Continuation to the file "foo.ser" and prints the following output:
next level next level next level next level next level run the file foo.ser
The following script deserializes the Continuation and executes it:
var k = deserialize("foo.ser");
k();
Evaluating the second script produces the following output:
restarted(6): Mon May 20 19:03:12 PDT 2002 restarted(5): Mon May 20 19:03:12 PDT 2002 restarted(4): Mon May 20 19:03:12 PDT 2002 restarted(3): Mon May 20 19:03:12 PDT 2002 restarted(2): Mon May 20 19:03:12 PDT 2002 restarted(1): Mon May 20 19:03:12 PDT 2002
SeparationOfLogicAndContent
This RT was send to the cocoon-users mailing list by Alan Hodgkinson. It was put here in order to create further discussion on best practices and design pattern for complex Cocoon-based web applications.
MalcolmCleaton: I fixed a couple of typos; hope nobody minds. Thought I'd mention it since the context here is of a direct quote.
----
Abstract
I present a way of naming links form actions that allow you to separate links and forms from the next page to display. I then raise questions about processing web application events, specifically how to do this within the existing Cocoon pipeline mechanism and maintain a separation between processing of the input from one page from the generation and display of the next page. I then say that Cocoon is great.
Introduction
First Michael Edge wrote:
> I have a question regarding the use and purpose of XSP. I \\ > believe that XSP is an attempt to separate content/logic \\ > from presentation \\
Then Leigh Dodds wrote:
> I've been thinking along the same lines recently, and am \\ > still in two minds. \\
Join the club. Cocoon is just fine at separating presentation from content and logic. The problem is the separation of the content and logic.
I am relatively new to Cocoon and from what I've read of the documentation and in the mail archive, it seems that Cocoon is not yet ideal for web applications that have complex behavior based on user input (e.g. the page flow changes based on the data the user enters and the results of state changes in the session and database/business-object layer). It's possible to implement such applications with Cocoon, but it doesn't (yet) seem to be Cocoon's strength.
The yet to be implemented flowmap mentioned in the cocoon-dev mailing list may be the solution we are looking for. (Seach for 'flowmap' in the cocoon-dev mailing list. The flowmap is not yet fully defined, which means that now is the time to provide your input).
It's About Control
Thinking in Model-View-Controller (MVC) terms, the problem is how to separate the controller from the view/model. Note that we don't need to worry too much about mixing up the model and view, given Cocoon's flexible handling of XML data via pipelines.
Mapping MVC to web applications is, in my mind, a priori, a mess, due to the fact that the requests (events) the web application receives must ultimately be encoded as links and form actions in the HTML presented to the user. Thus some mixing of view and control is required. In a sense, we are trying to make the best of a bad situation.
Specifically, you have to embed links and form actions that define the input events to the controller directly in the in web pages. There are ways arund this, but before I present a solution let's look at the types of input events we need to consider.
Links in Content
A typical web application the links can be divided into the following categories:
- Gotos: Links that simply take you to another part of
the application or web site. They typically make no state change, except perhaps to note in a sessinon variable that you are now on a different page. These are typically simple links.
- Content Display: Links that display statically or
dynamically generated data, but cause no state change. This includes search operations, and links/buttons that display 'additional' information. Typically these are links and buttons that include one or more key values E.g. an id value or form data that identifies the record(s) to display or present in the next page or form.
- State changes: Links that cause a state change. This
includes business object and session state changes. These can be quite complex operations. Often these are buttons associated with form data that the user enters.
How can you generate the embedded links and form actions in web content without creating explicit dependencies between the web pages (and also allow reuse of page components via Cocoon's aggregation)? The answer is to change the semantics of links and form actions.
Goto Considered Harmful, Come-From is Fine
Instead of having the link tell the controller where it wants to go, which is the controller's responsibility, it should merely tell the controller which button/link it 'is' or, in other words, where it is 'coming from'. For example, instead of a button having form action named:
/cgi-bin/doUserUpdateSubmit.pl
which says what the web server should do, you would have:
/userUpdateForm/changeName/submitButton
Which tells the controller exactly where the button was pressed (and consequently what parameters it should expect) and allows the controller the freedom to decide where to go next. More importantly, the developer is free to rearrange and reuse the pages and page components without changing any of the links or form actions. All that is required is to maintain a mapping of the events and states to their action routines (this might be stored in something called, say, a flowmap :)
The whole point of this is to define a 'URI space' that enables you to specify, independent of physical location or file naming, all the application events that the controller must respond to. In other words, all the buttons and links that the web application contains.
Obviously you must also define the fields and parameters associated with the buttons and links, but at least you have turned all the links and form actions into events and dissociated them from the application flow.
So What About Cocoon?
So far this all theory. The practical problem is:
How do you do this in Cocoon?
<disclaimer> Being a Cocoon, newbie, I quickly reach my limits and look to others to supply helpful suggestions. That said, here are some ideas. </disclaimer>
In the JSP/Servlet world, an obvious answer is the single servlet solution mentioned by Leigh, where you have one servlet that implements all the control logic and dispatches to JSP pages for display. In servlet based applications you're free to write as many servlets as you want. In practice, pretty much everybody only writes one, which acts as a controller.
There's probably going to be a parallel in Cocoon development world. Perhaps we'll all end up writing a single master pipeline that calls 'internal-only' pipeline components to do all the generation and display work.
How to implement the controller? Actions seem a reasonable choice. As explained in the Cocoon documentation, you can implement an action that sets one or more sitemap parameters that enable you to delegate the to the appropriate view based on the processing of the request parameters, session variables and business objects. This would require having the 'master pipeline' described above.
The Controller could be implemented as a generic class implementing Action, which could read an XML config file (called the flowmap or FSM-config). The config file would contain, in some form, the matrix of application states and events and their mappings to the 'action pipelines'.
It might be possible to encode the controller configuration directly in the pipeline portion of the sitemap, but I'm not sure that you want to include all that 'application code', (which is what this information really is, in the sitemap.
Pipelines Are So Cool That I Want Two of Them
How can I separate the processing of the page form data from the page I just left, from the generation and display of the page I am going to present next?
Cocoon, with its powerful pipelines, seduces you into implementing your processing and display in a single pipeline (which might call sub-pipelines, but the point is that it's a single event chain). The problem is, and you see it mentioned in the mailing lists, that once the pipeline is started, and the SAX events are flying towards the user, there's no turning back or possibility for switching to another pipeline if you discover or decide you want to display something else.
When responding to a request I'd like to use one pipeline composed of XSP logicsheets to perform the event processing and then be able to switch to another pipeline to generate the next page. All this should be managed by my controller which gets to pick the second pipeline based on the processing results from the first.
Is this even possible in Cocoon? Are redirects an answer? My reading of the mail archives leads me to believe that this is probably not the correct solution. But perhaps there's some other technique.
It is of course possible to implement all the page processing in Java, in an action class, which chooses the next page, but then I loose all the advantages of the logicsheets. Is there some trick we can use so that we 'have it all'?
I eagerly await your feedback and suggestions.
Best wishes to you all,
Alan Hodgkinson
TextFormattingRules
When you've figured out how the editor works, then you should read WikiEtiquetteso that you would know how to use your newly acquired skills...
Quick reference
---- = Make a horizontal ruler. Extra '-' is ignored.
\\ = force a line break, \\\=force line break and clear.
[link] = creates a hyperlink to an internal WikiPage called 'Link'.
[this is also a link] = creates a hyperlink to an internal WikiPage called
'ThisIsAlsoALink'.
[click here|link] = creates a hyperlink to an internal WikiPage called
'Link', but displays the text 'click here' to the
user instead of 'Link'.
[1] = Makes a reference to a footnote numbered 1.
[#1] = Marks the footnote number 1.
[[link] = creates text '[link]'.
!heading = small heading with text 'heading'
!!heading = medium heading with text 'heading'
!!!heading = large heading with text 'heading'
''text'' = prints 'text' in italic.
__text__ = prints 'text' in bold.
{{text}} = prints 'text' in monospaced font.
* text = makes a bulleted list item with 'text'
# text = makes a numbered list item with 'text'
;term:ex = makes a definition for 'term' with the explanation 'ex'
Writing text
You don't need to know anything about the Wiki text formatting rules to use Wiki. Just write normal text, and then use an empty line to mark a paragraph. It's just like writing an email.
You can always Edit this page (look at the left sidebar) to see how the different effects on this page are used.
Hyperlinks
The link can also be a direct URL starting with http:, ftp:, mailto:, https:, or news:, in which case the link points to an external entity. For example, to point at the java.sun.com home page, use [[http://java.sun.com], which becomes http://java.sun.com/or [[Java home page|http://java.sun.com], which becomes Java home page.
To add a new page you just create a link to it from somewhere else. After all, there isn't much point in having a page if you can't access it! You'll then see a small question mark after the page name when you return to that page. Then click on it and you have created a new page!
It's allowed to use almost any kind of characters inside a WikiName, as long as they are letters or numbers.
Note also that this Wiki can be configured to support standard CamelCase linking (if it's supported, the word CamelCase should be a link). It's off by default, but if your friendly administrator has turned it on, then well, CamelCase all you want =).
Footnotes
These are a special kind of hyperlink. By using nothing but a number inside a hyperlink you create a reference to a footnote, like this [[1], which creates a footnote [1] . To make the actual footnote, you just put a [[#1] where you want that footnote to point at. Look below to find the footnote.
You can also make a named footnote, just as if you were doing a normal hyperlink. For example, this refers to the same footnoteFootnote number 1as the footnote above, but this refers to another footnote [2] .
InterWiki links
You can also do links between different Wikis without knowing the URL. Just use a link in the form [[Wiki:WikiPage] and JSPWiki will create a link for you. For example, this link points to the JSPWiki TextFormatting rules. Check the SystemInfopage for more information on which Wiki links are available.
If an InterWiki link is not supported, you'll get a notification of it on the page when you save your page.
Adding pictures
For security reasons uploading images is not permitted, but you can embed any image in the wiki code by putting the image available somewhere on the web in one of the allowed formats, and then just linking to it. For example, this is an inlined PNGimage: http://www.ecyrd.com/~jalkanen/test.png.
If you specify a link text ([[this one here|http://example.com/example.png]) it becomes the ALT text for those who either can't or don't want to view images.
The list of accepted image types depends on the Wiki. See the SystemInfo page for a list of the different image types.
Bulleted lists
Use an asterisk (*) in the first column to make bulleted lists. Use more asterisks for deeper indentation. For example:
* One * Two * Three ** Three.One
creates
- One
- Two
- Three
- Three.One
Numbered lists
Just like with bulleted lists, but use a hash (#) instead of the asterisk. Like this:
# One # Two # Three ## Three.One
creates
- One
- Two
- Three
- Three.One
If you want to write the list item on multiple lines, just add one or more spaces on the next line and the line will be automatically added to the previous item. If this sounds complicated, edit this page for an example, below.
- This is a single-line item.
- This is actually a multi-line item.
We continue the second sentence on a line on a line of its own. We might as well do a third line while we're at it... Notice, however, as all these sentences get put inside a single item!
- The third line is again a single-line item for your convinience.
Definition lists and comments
A simple way to make definition lists is to use the ';:' -construct:
;Construct:Something you use to do something with
Another nice use for the ';:' is that you can use it to comment shortly on other people's text, by having an empty 'term' in the definition, like this:
;:''Comment here.''
Which would be seen as ;:Comment here.
Text effects
You may use boldtext or italictext, by using two underscores (_) and two single quotes ('), respectively. If you're on a Windows computer, make sure that you are using the correct quote sign, as there is one that looks the same, but really isn't.
Preformatted text
If you want to add preformatted text (like code) just use three consecutive braces ({) to open a block, and three consecutive braces (}) to close a block. Edit this page for an example.
Tables
You can do simple tables by using using pipe signs ('|'). Use double pipe signs to start the heading of a table, and single pipe signs to then write the rows of the table. End with a line that is not a table.
For example:
|| Heading 1 || Heading 2 | ''Gobble'' | Bar | [Main] | [SandBox]
gives you the following table. Note how you can use links also inside tables.
| Heading 1 | Heading 2 |
|---|---|
| Gobble | Bar |
| Main | SandBox |
Conflicts
If someone happens to edit the same page as you at the same time, JSPWiki will prevent you from doing changes and show a conflict page instead. Sorry to say, but the first one to make changes wins...
A word of warning:If you use the Back button of your browser to go into the Edit page, you will almost certainly get a conflict. This is because the browser thinks its still editing an earlier copy of the page.
Deleting pages
This is not possible. You can, of course, remove all the links to that page, which makes it inaccesible. Or you can email the administrator, and I'll remove the page.
Adding new pages
Create a link that points to a new (not existing) page using its WikiName. Click that new link, which should now have a question mark (?) suffix and you will get an editor for the new page. -- Asser
Attaching files
If the administrator of the wiki has configured it, there is a "Attach file..." link at the bottom of every page. Clicking it allows you to attach files into pages. For more information, please see WikiAttachments.
Inserting variables
There are many possible variables you can insert on a page. The basic form is:
[[{$variablename}],
where variablenameis the name of the variable you want to insert. Note that variable names are case-insensitive - that is, "pagename" is the same as "paGeNamE" and "PageName".
You can see the list of available of variables at WikiVariables.
Inserting plugins
The basic incantation to insert a plugin looks like this:
[[{INSERT <plugin class> WHERE param1=value, param2=value, ...}]
There is more information in JSPWikiPlugins.
----
WoodySample
A simple Woody example
Here we will show how to create a form by showing a "registration form" example.
Here is a screenshot of the form we're going to create:
http://outerthought.net/~bruno/images/woody_registrationform_initial.png
This example is included in the Woody block, you may want to play with it first before reading the explanation below.
The form definition file
Below the form definition file is displayed. This lists all the widgets in the form, together with their configuration information.
<wd:form
xmlns:wd="http://cocoon.apache.org/woody/definition/1.0"
xmlns:i18n="http://apache.org/cocoon/i18n/2.1">
<wd:field id="name" required="true">
<wd:label>Name:</wd:label>
<wd:datatype base="string">
<wd:validation>
<wd:length min="2"/>
</wd:validation>
</wd:datatype>
</wd:field>
<wd:field id="email" required="true">
<wd:label>Email address:</wd:label>
<wd:datatype base="string">
<wd:validation>
<wd:email/>
</wd:validation>
</wd:datatype>
</wd:field>
<wd:field id="age">
<wd:label>Your age:</wd:label>
<wd:datatype base="long">
<wd:validation>
<wd:range min="0" max="150"/>
</wd:validation>
</wd:datatype>
</wd:field>
<wd:field id="password" required="true">
<wd:label>Password:</wd:label>
<wd:datatype base="string">
<wd:validation>
<wd:length min="5" max="20"/>
</wd:validation>
</wd:datatype>
</wd:field>
<wd:field id="confirmPassword" required="true">
<wd:label>Re-enter password:</wd:label>
<wd:datatype base="string">
<wd:validation>
<wd:assert test="password = confirmPassword">
<wd:failmessage>The two passwords are not equal.</wd:failmessage>
</wd:assert>
</wd:validation>
</wd:datatype>
</wd:field>
<wd:booleanfield id="spam">
<wd:label>Send me spam</wd:label>
</wd:booleanfield>
</wd:form>
All elements are in the Woody Definition (wd) namespace.
The children of the wd:form element identify widgets. As you can see, most of the widgets are "field" widgets. The "field" widget is the most important widget in Woody, so lets look at it in a bit more detail.
Field widget
A field widget can be associated with a datatype. The function of the datatype is to convert the string value entered by the user to a more specific type like a number or a date (and vice versa, convert them back to strings). The datatype will also perform the validation. (This split-up between "widget" and "datatype" is specific for the field widget -- it is perfectly possible to make widgets that have nothing to do with datatypes). In this way, a field widget contains strongly-typed data. For example, if you associated a "long" datatype with a field widget, then you can be sure that when you retrieve the widget's value, you will get a Long object (that is, if the form was validated successfully).
The reasoning behind the "base" attribute on the wd:datatype element is that you are actually defining a new datatype, based on the built-in "string" or "long" datatype, and customise it with validation rules (and possibly other parameters).
A datatype can furthermore be associated with a selection list. This makes that the field widget could be rendered either as a textbox or a list, depending on whether its datatype has a selection list. For an example of selection lists, see the "Form1" example provided with Woody.
http://outerthought.net/~bruno/images/field_datatype_relation.png
If we wouldn't make this datatype and selection list associations, we would need to create specific widgets for each possible combination: StringField, LongField, DateField, StringSelectionList, LongSelectionList, ...
Labels
A nice feature is that the wd:label tags can contain mixed content. On the one hand, this can be used to provide rich formatting in the label. But it also enables you to put i18n-elements in there, to be interpreted by the I18nTransformer. This way, internationalisation is done using standard Cocoon techniques.
The sitemap
Here is the relevant sitemap fragment for handling the registration form:
<map:match pattern="registration">
<map:select type="request-method">
<map:when test="GET">
<map:act type="woody-make-form">
<map:parameter name="form-definition" value="forms/registration.xml"/>
<map:parameter name="attribute-name" value="registrationform"/>
</map:act>
</map:when>
<map:otherwise>
<map:act type="woody-handle-form-submit">
<map:parameter name="form-definition" value="forms/registration.xml"/>
<map:parameter name="attribute-name" value="registrationform"/>
<map:generate type="serverpages" src="forms/registration_success.xsp"/>
<map:serialize/>
</map:act>
</map:otherwise>
</map:select>
<map:generate src="forms/registration_template.xml"/>
<map:transform type="woody">
<map:parameter name="attribute-name" value="registrationform"/>
</map:transform>
<map:transform type="i18n">
<map:parameter name="locale" value="en-US"/>
</map:transform>
<map:transform src="xsl/html/woody-default.xsl"/>
<map:transform type="i18n">
<map:parameter name="locale" value="en-US"/>
</map:transform>
<map:serialize/>
</map:match>
It works as follows:
- based on the request method (GET or POST), we decide whether this is a first-time request of the form or whether this is a form submit
- in case of a first-time request, the "woody-make-form" action is used, which will simply create a form instance and put it in a request attribute (this is a storage area that lasts for the duration of the request)
- in case of a form submit, we use the "woody-handle-form-submit" action. This action will also create a form instance, and let it process the request and validate it. If validation was successful, it returns non-null (so that the contents of the map:act element will be executed), otherwise it returns null (so that the pipeline below the map:act will be executed).
- in case of successful validation, we simply execute an XSP page. In "real-life" situations, this may also call an action, do a redirect, or whathever.
- in case validation failed, or in case the form is displayed for the first time, the pipeline below the selector is executed. How this works is described in the next section.
The WoodyTemplateTransformer
The publishing pipeline for the form starts from a template file. Here's the template for our registration form example:
<html xmlns:wt="http://cocoon.apache.org/woody/template/1.0">
<head>
<title>Registration form</title>
</head>
<body>
<h1>Registration</h1>
<form action="registration" method="POST">
<wt:widget-label id="name"/>
<wt:widget id="name"/>
<br/>
<wt:widget-label id="email"/>
<wt:widget id="email"/>
<br/>
<wt:widget-label id="age"/>
<wt:widget id="age"/>
<br/>
<wt:widget-label id="password"/>
<wt:widget id="password"/>
<br/>
<wt:widget-label id="confirmPassword"/>
<wt:widget id="confirmPassword"/>
<br/>
<wt:widget id="spam"/>
<wt:widget-label id="spam"/>
<br/>
<input type="submit"/>
</form>
</body>
</html>
The Woody-specific elements here are in the "Woody Template" (wt) namespace.
The wt:widget-label tag will cause the label of a widget to be inserted at the location of the tag. The wt:widget tag will cause the XML representation of a widget to be inserted at the location of that tag. The inserted XML will be in the "Woody Instance" (wi) namespace.
The XML representation of the widget will then be translated to HTML by an XSLT stylesheet (woody-default.xsl in our case). This XSLT only has to handle individual widgets, and not the page as a whole, and is thus not specific for one form but can be reused across forms.
For certain widgets it may be necessary to provide extra presentation hints, such as the width of a text box, the style of a selection list (drop down, radio buttons, ...) or class and style attribute values. This can be done by putting extra elements inside the wt:widget element. Currently there's not much implemented yet, but what's available will be documented in a reference section.
As an alternative to the template approach, you could also use the WoodyGenerator, which will generate an XML representation of the whole form, and style that with a custom-written XSLT.
XMLFormXindice
Overview
This How-To shows you how to use Xindice as the repository for XML resources from the XMLForm Framework. It requires prior knowledge of Cocoon XMLForm, XSLT, Schematron, Xindice and the XMLDB API.
Purpose
You will learn how to build a simple wizard type XMLForm that stores XML data into a Xindice collection.
Intended Audience
Cocoon users who want to learn how to store data obtained from XMLForms into Xindice.
Prerequisites
Cocoon must be running on your system. The steps below have been tested with Cocoon 2.1-dev. You will need the following:
- A servlet engine such as Tomcat.
- JDK 1.2 or later
- Xindice 1.0 installed (create a collection named
Artist)
- Cocoon 2.1 CVS to be installed with the command:
build -Dinclude.webapp.libs=true webapp
You will need to understand and be familiar with XSL, XForms, XPath, Schematron and Xindice. Some knowledge about JXPath and the XMLDB API would be helpful, too. If you are unfamiliar with these technologies, it is advised that you learn these related concepts first. If you are unfamiliar with XMLForm, check out the XMLForm Wizard How-Tofirst.
Steps
We will follow the needed steps in order to add a document like the one below to a Xindice collection named Artist.
<Artist id="pearljam"> <Name>Pearl Jam</Name> </Artist>
We will get the identifier and name data using a XMLForm and store them in Xindice. We will build this XMLForm very similar to the one in the XMLForm Wizard How-To.
Building the XMLForm files
Create the files and name them as specified below.
start.xform
<?xml version="1.0"?>
<document>
<h1>This is the New Artist Wizard!</h1>
<info>Steps from here on, will let you insert a new
Artist in the database.
</info>
<h3>
<a href="Artist.xform?cocoon-action-start=true">
Start!
</a>
</h3>
</document>
artist.xform
<?xml version="1.0"?>
<document xmlns:xf="http://xml.apache.org/cocoon/xmlform/2002">
<xf:form id="artist-insert" view="artist" action="Artist.xform" method="post">
<xf:caption>New Artist</xf:caption>
<error>
<xf:violations class="error"/>
</error>
<xf:textbox ref="/Artist/@id">
<xf:caption>Artist identifier:</xf:caption>
</xf:textbox>
<xf:textbox ref="/Artist/Name">
<xf:caption>Artist Name:</xf:caption>
</xf:textbox>
<xf:submit id="prev" class="button">
<xf:caption>Prev</xf:caption>
<xf:hint>Go to previous page</xf:hint>
</xf:submit>
<xf:submit id="next" class="button">
<xf:caption>Next</xf:caption>
<xf:hint>Go to next page</xf:hint>
</xf:submit>
</xf:form>
</document>
end.xform
<?xml version="1.0"?>
<document>
<h1>You have reached the last page!</h1>
<info>
You have inserted a New Artist successfully.
</info>
<h3>
<a href="Artist.xform">Go to home page.</a>
</h3>
</document>
error.xform
<?xml version="1.0"?>
<document>
<h1>
You have reached the last page of the New Artist Wizard!
</h1>
<info>
There have been problems and the Artist could not be added to the database.
Please try again.
</info>
<h3>
<a href="Artist.xform">Please, start again.</a>
</h3>
</document>
Validation
For the sake of simplicity we just validate one property against one condition. We require the identifier to be at least two characters in length; the validation file, artist-validator.xmlis as follows:
<?xml version="1.0"?>
<schema ns="http://xml.apache.cocoon/xmlform" xmlns="http://www.ascc.net/xml/schematron">
<phase id="artist">
<active pattern="artval"/>
</phase>
<pattern name="Artist Identifier Validation" id="artval">
<rule context="/Artist/@id">
<assert test="string-length(.) > 1">
Artist Name should be at least 2 characters.
</assert>
</rule>
</pattern>
</schema>
Extended Validation
There could be more complicated rules in Schematron but we could also require the identifier to be unique in the database. In this case we should query the database and see if it already exists. If so, a new violation can be added to the form. Since these kinds of violations are out of the scope of Schematron, these operations should be accomplished in the Action using Java code.
The Model
Here is where we will take a different approach to the one in the XMLForm Wizard How-To. We will not use a separate Bean. In order to have an XML document model we will create an XML file with the empty structure we want to fill with data from the form. Create the file artist-model.xmland fill it with:
<Artist id=""> <Name></Name> </Artist>
Persistence for the data will be accomplished by using a JXPath Container. The XML document model created above will be loaded into the Container. We will use the xmlform-modelparameter in the sitemap to point to the XML file.\\ The Container is created and manipulated by the Action in its getFormModel()method, so we need to override it and write it this way (we'll see the whole Action in the next step):
/**
* Extract xmlform-model parameter and
* instantiate a new form model from it.
*/
protected Object getFormModel() {
//to load the XML model
Container DOMModel = null;
Source modelSrc = null;
//this parameter holds the name of the empty XML document representing the model
String modelFileName = getParameters().getParameter("xmlform-model", null);
if(modelFileName==null) return null;
try {
modelSrc = getSourceResolver().resolveURI(modelFileName);
DOMModel = new XMLDocumentContainer(new StreamSource(modelSrc.getInputStream()));
return DOMModel;
}
catch ( Exception e) {
throw new CascadingRuntimeException( " Failed instantiating form model ", e );
}
finally {
getSourceResolver().release(modelSrc);
}
}
The Action
In this Action we have integrated the Xindice handling code, getting the data from the model and storing it in Xindice. The Action that controls this form is ArtistAction.java:
package com.simbiosystems.cocoon.xmlform.xindice.howto;
import java.util.Arrays;
import java.util.Map;
import javax.xml.transform.stream.StreamSource;
import org.apache.avalon.framework.CascadingRuntimeException;
import org.apache.cocoon.acting.AbstractXMLFormAction;
import org.apache.cocoon.components.validation.Violation;
import org.apache.cocoon.components.xmlform.Form;
import org.apache.cocoon.components.xmlform.FormListener;
import org.apache.commons.jxpath.Container;
import org.apache.commons.jxpath.XMLDocumentContainer;
import org.apache.excalibur.source.Source;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* This action handles XMLForms for the Artist data
*/
public class ArtistAction extends AbstractXMLFormAction implements FormListener {
// different form views participating in the form
final String VIEW_START = "start";
final String VIEW_ARTIST = "artist";
final String VIEW_END = "end";
final String VIEW_ERROR = "error";
// action commands used in the wizard
final String CMD_START = "start";
final String CMD_NEXT = "next";
final String CMD_PREV = "prev";
//constant for the XML manipulation, it holds the full name of the collection
//to be used for storing the data
final String xindiceSubCol = "/Artist";
/**
* Extract xmlform-model action parameter and
* instantiate a new form model from it.
*
* In this case it uses a JXPath Container as the model and
* there is no need for a separate model Bean.
*
* @return Form the form object this action works with.
*
*/
protected Object getFormModel() {
//to load the XML model
Container DOMModel = null;
Source modelSrc = null;
//this parameter holds the name of the empty XML document representing the model
String modelFileName = getParameters().getParameter("xmlform-model", null);
if(modelFileName==null) return null;
try {
modelSrc = getSourceResolver().resolveURI(modelFileName);
DOMModel = new XMLDocumentContainer(new StreamSource(modelSrc.getInputStream()));
return DOMModel;
}
catch ( Exception e) {
throw new CascadingRuntimeException( " Failed instantiating form model ", e );
}
finally {
getSourceResolver().release(modelSrc);
}
}
/**
* Invoked after form population
* Take appropriate action based on the command
*
*/
public Map perform () {
// set the page control flow parameter
// according to the validation result
if ( getCommand().equals(CMD_NEXT) && getForm().getViolations () != null ) {
// errors, back to the same page
return page( getFormView() );
}
else {
// validation passed
// continue with control flow
// clear validation left overs in case the user
// did not press the Next button
getForm().clearViolations();
// get the user submitted command
String command = getCommand();
// get the form view which was submitted
String formView = getFormView();
// apply control flow rules
if (formView.equals (VIEW_ARTIST)) {
if (command.equals(CMD_NEXT)) {
//extended validation
//test if the ID already exists in the DB
String artistName = (String)getForm().getValue("/Artist/@id");
try {
XindiceManager xi = new XindiceManager();
Node result = xi.find(xindiceSubCol, "/Artist[@id='"+ artistName +"']", "Artist");
//if we do not get null the element with that ID
//already existed and we add the violation
if (result!=null) {
Violation v = new Violation();
v.setMessage("already exists in the database, please choose another one");
v.setPath("/Artist/@id");
Violation[] va = { v };
getForm().addViolations(Arrays.asList((Object[])va));
//the ID already exists, back to the same
//page to correct the error
return page(VIEW_ARTIST);
}
}
catch (Exception e) {
getLogger().error("Cannot establish a connection to the DB", e);
}
//everything went fine, add the document to the database
try {
addDocument();
}
catch(Exception e) {
//there were errors, send it to the error page
getLogger().error("Cannot add DOM document to the database");
return page(VIEW_ERROR);
}
return page( VIEW_END);
}
if (command.equals(CMD_PREV)) {
return page(VIEW_START);
}
}
}
// should never reach this statement
return page( VIEW_START );
}
/**
* The first callback method which is called
* when an action is invoked.
*
* It is called before population.
* @return null if the Action is ready to continue.
* an objectModel map which will be returned
*/
protected Map prepare() {
if ( getCommand() == null ) {
return page(VIEW_START);
}
else if ( getCommand().equals(CMD_START)) {
// reset state by removing old form if one exists
Form.remove( getObjectModel(), getFormId() );
getForm().addFormListener( this );
return page(VIEW_ARTIST);
}
// get ready for action
// if not ready return page("whereNext");
return null;
}
/**
* Add the document to the database
*
*/
public void addDocument() throws Exception {
try {
//add the document to the database
XindiceManager xi = new XindiceManager();
//needs the DocumentRoot of the Container as a DOM Node
xi.add(xindiceSubCol, ((Document)(((XMLDocumentContainer)(getForm().getModel())).getValue())).getDocumentElement(), null);
}
catch (Exception e) {
getLogger().error("DOM Document could not be created", e);
throw e;
}
}
}
We had to use an ugly casting mechanism in the addDocumentmethod but that's what we have by now. In future JXPath versions this will be easier to accomplish.
The helper class
In order to make this work we need to use a helper class. This class uses the XMLDB API to connect to Xindice and make the operations available to the Action. You should extend it to add more operations. The helper class XindiceManager.javais as follows:
/**
* Helper class for Xindice related operations
*
*/
package com.simbiosystems.cocoon.xmlform.xindice.howto;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Database;
import org.xmldb.api.base.Resource;
import org.xmldb.api.base.ResourceIterator;
import org.xmldb.api.base.ResourceSet;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.XMLResource;
import org.xmldb.api.modules.XPathQueryService;
public class XindiceManager {
private static final String driver = "org.apache.xindice.client.xmldb.DatabaseImpl";
private static final String rootCollection = "xmldb:xindice:///db/";
/**
* Constructor
*
*/
public XindiceManager() {
}
/**
* Search for a document in the DB. If not found, return null.
*
* @param subCol name of the subCollection to query if any. If blank or null,
* queries go against the rootCollection.
* @param xpath XPath expression for the query, if none, it returns the whole Collection
* @param resultRootelement name of the root element for the DOM document which will
* wrap the results
* @return a DOM Node with the matched documents
*
*/
public Node find(String subCol, String xpath, String resultRootElement)
throws Exception {
//prepare DOM document
DOMImplementation impl;
DocumentBuilder builder;
try {
// Find the implementation
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware( false );
factory.setValidating ( false );
builder = factory.newDocumentBuilder();
impl = builder.getDOMImplementation();
}
catch (Exception ex) {
throw new RuntimeException("[XindiceManager.find]: Failed to initialize DOM factory. Root cause: \n" + ex);
}
//create the Document which will hold the results
Document resultDoc = impl.createDocument(null, resultRootElement, null);
Node root = resultDoc.getDocumentElement();
//And now the Xindice part
Collection col = null;
String strData = null;
try {
Class c = Class.forName(driver);
Database database = (Database) c.newInstance();
DatabaseManager.registerDatabase(database);
String localCol = rootCollection + subCol;
col = DatabaseManager.getCollection(localCol);
//Make the XPath query and get the resources
XPathQueryService service =
(XPathQueryService) col.getService("XPathQueryService", "1.0");
ResourceSet resultSet = service.query(xpath);
// Iterate the xpath results and add each of them to the main Document
ResourceIterator iterator = resultSet.getIterator();
//if not resources are present, just return null
if(!iterator.hasMoreResources()) return null;
while(iterator.hasMoreResources()) {
Resource r = iterator.nextResource();
Element resElement = ((Document)((XMLResource)r).getContentAsDOM()).getDocumentElement();
// Remove unwanted attributes
resElement.removeAttribute("src:col");
resElement.removeAttribute("src:key");
resElement.removeAttribute("xmlns:src");
// Add this result to the root element
Element importedElement = (Element)resultDoc.importNode(resElement, true);
root.appendChild(importedElement);
}
//return the Document root
return root;
}
catch (Exception e) {
System.err.println("[XindiceManager.find]: Find Exception occured :" + e.getMessage());
throw e;
}
finally {
if (col != null) {
col.close();
}
}
}
/**
* Add a document to the DB. If a key is not passed, it generates a unique one.
* This version uses a DOM Document.
* @param subCol name of the subCollection in which to add the resource if any. If blank or
* null, insertions go against the rootCollection.
* @param document DOM document to be inserted
* @param key unique key for the resource to be created, if none, one is created on the fly
*/
public void add(String subCol, Node document, String key) throws Exception {
Collection col = null;
Node xmldoc = null;
try {
Class c = Class.forName(driver);
Database database = (Database) c.newInstance();
DatabaseManager.registerDatabase(database);
col = DatabaseManager.getCollection(rootCollection + subCol);
XMLResource document_ = (XMLResource) col.createResource(null, "XMLResource");
document_.setContentAsDOM(document);
col.storeResource(document_);
}
catch (XMLDBException e) {
System.err.println("("[XindiceManager.add]: Add Exception occured " + e.errorCode);
throw e;
}
finally {
if (col != null) {
col.close();
}
}
}
}
The Sitemap
Remember you should have the XMLFormTransformer defined like this:
<map:transformer name="xmlform" src="org.apache.cocoon.transformation.XMLFormTransformer" logger="xmlform"/>
Then, you must declare the Action to be used in the correspondent section:
<map:action logger="xmlform" name="ArtistAction" src="com.simbiosystems.cocoon.xmlform.xindice.howto.ArtistAction"/>
The Pipeline
<!-- XMLForms<->Xindice pipeline -->
<map:pipeline>
<map:match pattern="Artist.xform">
<map:act type="ArtistAction">
<!-- XMLForm parameters for the Action -->
<!-- Notice how we use an XML file as the model -->
<map:parameter name="xmlform-validator-schema-ns" value="http://www.ascc.net/xml/schematron"/>
<map:parameter name="xmlform-validator-schema" value="artist/artist-validator.xml"/>
<map:parameter name="xmlform-id" value="artist-insert"/>
<map:parameter name="xmlform-scope" value="session"/>
<map:parameter name="xmlform-model" value="artist/artist-model.xml"/>
<!-- XMLForm document, {page} comes from Action -->
<map:generate src="artist/{page}.xform"/>
</map:act>
<!-- populating the doc with model instance data -->
<map:transform type="xmlform"/>
<!-- look and feel of the form controls -->
<map:transform src="styles/wizard2html.xsl"/>
<!-- Transforming the XMLForm controls to HTML -->
<map:transform src="styles/xmlform2html.xsl"/>
<!-- sending the HTML back to the browser -->
<map:serialize type="html"/>
</map:match>
</map:pipeline>
Depending on where you have Cocoon installed and where you have configured the files in this howto, you could make a request to a URL like http://localhost:8080/cocoon/Artist.xformand start using the Form.
Final Considerations
Making the operations from the Action using the helper class seems to be the easiest way. You can think of other ways or other operations using other Xindice tools available in Cocoon such as the pseudo protocol. For example, you could use it to query the DB for data that could be stored in a sitemap parameter. You could then get the data from the Action and use it to fill a selectbox in the form.\\ We did not mentioned other operations such as updates. This can be also accomplished this way. For example, if you want to edit the data we just stored, you could load it in the Container at the beginning by using the findmethod of the helper class. This way you get a filled form in the next step ready for editing. Other ways or interacting with the repository include the XMLDB Transformer. Since it uses XUpdate alike syntax, you could format a XML String for it in the last step of the Action, making it available to the sitemap then, so the Transformer could get it and make the operations.\\ I'm sure you can think of more different ways. I encourage you to contribute them if you have tested it succesfully.\\ This how-to was refactored quite a bit from its original version which used a regular Javabean as a wrapper for a DOM Node where the data were persisted. This new version version is much more elegant and short and it wouldn't be possible without some ideas exchanged with Ivelin Ivanov. Anyway if you want to see the previous version to compare we still have a copy at XMLFormXindiceOldVersion.
Summary
This How-To makes possible the use of Xindice from XMLForms in order to add data to the repository. You learned how to connect to the DB from the Action, and how to make complex validation using information stored in the DB. I hope it was helpful for you.
XMLFormXindiceOldVersion
Overview
This How-To shows you how to use Xindice as the repository for XML resources from the XMLForm Framework. It requires prior knowledge of Cocoon XMLForm, XSLT, Schematron, Xindice and the XMLDB API.
Purpose
You will learn how to build a simple wizard type XMLForm that stores XML data into a Xindice collection.
Intended Audience
Cocoon users who want to learn how to store data obtained from XMLForms into Xindice.
Prerequisites
Cocoon must be running on your system. The steps below have been tested with Cocoon 2.1-dev. You will need the following:
- A servlet engine such as Tomcat.
- JDK 1.2 or later
- Xindice 1.0 installed (create a collection named
Artist)
- Cocoon 2.1 CVS to be installed with the command:
build -Dinclude.webapp.libs=true webapp
You will need to understand and be familiar with XSL, XForms, XPath, Schematron and Xindice. Some knowledge about the XMLDB API would be helpful, too. If you are unfamiliar with these technologies, it is advised that you learn these related concepts first. If you are unfamiliar with XMLForm, check out the XMLForm Wizard How-Tofirst.
Steps
We will follow the needed steps in order to add a document like this:
<Artist id="pearljam"> <Name>Pearl Jam</Name> </Artist>
to the Xindice root collection. We will get the identifier and name data using a XMLForm and store them in Xindice. We will build this XMLForm very similar to the one in the XMLForm Wizard How-To.
Building the XMLForm files
Create the files and name them as specified below.
start.xform
<?xml version="1.0"?>
<document>
<h1>This is the New Artist Wizard!</h1>
<info>Steps from here on, will let you insert a new
Artist in the database.
</info>
<h3>
<a href="Artist.xform?cocoon-action-start=true">
Start!
</a>
</h3>
</document>
artist.xform
<?xml version="1.0"?>
<document
xmlns:xf="http://xml.apache.org/cocoon/xmlform/2002">
<xf:form id="form-insert" view="artist"
action="Artist.xform" method="post">
<xf:caption>New Artist</xf:caption>
<error>
<xf:violations class="error"/>
</error>
<xf:textbox ref="/artistDocument/@id">
<xf:caption>Artist identifier:</xf:caption>
</xf:textbox>
<xf:textbox ref="/artistDocument/Name">
<xf:caption>Artist Name:</xf:caption>
</xf:textbox>
<xf:submit id="prev" class="button">
<xf:caption>Prev</xf:caption>
<xf:hint>Go to previous page</xf:hint>
</xf:submit>
<xf:submit id="next" class="button">
<xf:caption>Next</xf:caption>
<xf:hint>Go to next page</xf:hint>
</xf:submit>
</xf:form>
</document>
end.xform
<?xml version="1.0"?>
<document>
<h1>You have reached the last page!</h1>
<info>
You have inserted a New Artist successfully.
</info>
<h3>
<a href="Artist.xform">Go to home page.</a>
</h3>
</document>
error.xform
<?xml version="1.0"?>
<document>
<h1>
You have reached the last page of the New Artist Wizard!
</h1>
<info>
There have been problems and the Artist could not be added to the database.
Please try again.
</info>
<h3>
<a href="Artist.xform">Please, start again.</a>
</h3>
</document>
Validation
For the sake of simplicity we just validate one property against one condition. We require the identifier to be at least two characters in length; the validation file, artist-validator.xmlis as follows:
<?xml version="1.0"?>
<schema ns="http://xml.apache.cocoon/xmlform"
xmlns="http://www.ascc.net/xml/schematron">
<phase id="artist">
<active pattern="artval"/>
</phase>
<pattern name="Artist Identifier Validation"
id="artval">
<rule context="/artistDocument/@id">
<assert test="string-length(.) > 1">
Artist Name should be at least 2 characters.
</assert>
</rule>
</pattern>
</schema>
Extended Validation
There could be more complicated rules in Schematron but we could also require the identifier to be unique in the database. In this case we should query the database and see if it already exists. If so, a new violation can be added to the form. Since these kinds of violations are out of the scope of Schematron, these operations should be accomplished in the Action using Java code.
Model Bean
Persistence for the data will be accomplished by this bean. In order to store the mentioned XML structure we will use a DOM Node. You can also try JXPath Containers as suggested by Ivelin. The model bean ArtistBean.javais as follows:
package com.simbiosystems.cocoon.xmlform.xindice.howto;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
/**
*
* A sample domain object used as a Form model.
* DOM Nodes, are handled correctly by the
* framework when referenced via JXPath.
*
*/
public class ArtistBean {
//Holds the DOM Node that will be filled by the form and
//added later to Xindice
private Node artistDocument;
public ArtistBean () {
//create the document model
initModel();
}
/**
* Creates an empty DOM Node which holds an empty copy
* of an Artist document like this:
* <Artist id="">
* <Name></Name>
* </Artist>
* The data from the form is stored in it.
*/
public void initModel() {
DOMImplementation impl;
try {
// Find the implementation
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating ( false );
DocumentBuilder builder = factory.newDocumentBuilder();
impl = builder.getDOMImplementation();
}
catch (Exception ex) {
throw new RuntimeException("Failed to initialize DOM factory. Root cause: \n" + ex);
}
//You can choose any altenative method for this...
//Create the node for the root, 'Artist'
Document doc = impl.createDocument( null, "Artist", null);
Node artistRoot = doc.getDocumentElement();
//add a 'id' attribute
Attr id = doc.createAttribute ( "id" );
id.setValue ( "" );
NamedNodeMap nmap = artistRoot.getAttributes();
nmap.setNamedItem ( id );
//add a 'Name' child
Node artistName = doc.createElement ( "Name" );
Text text = doc.createTextNode( "" );
description.appendChild(text);
artistRoot.appendChild( artistName );
artistDocument = artistRoot;
}
/**
* Gets the artistDocument
* @return Returns a Node
*/
public Node getArtistDocument() {
return artistDocument;
}
/**
* Sets the artistDocument
* @param artistDocument The artistDocument to set
*/
public void setArtistDocument(Node artistDocument) {
this.artistDocument = artistDocument;
}
}
The Action
In this Action we have integrated the Xindice handling code, getting the data from the model (from the DOM Node property) and storing it in Xindice. The Action that controls this form is ArtistAction.java:
package com.simbiosystems.cocoon.xmlform.xindice.howto;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.Constants;
import org.apache.cocoon.acting.AbstractXMLFormAction;
import org.apache.cocoon.components.validation.Violation;
import org.apache.cocoon.components.xmlform.Form;
import org.apache.cocoon.components.xmlform.FormListener;
import org.apache.cocoon.environment.Redirector;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.environment.Session;
import org.apache.cocoon.environment.SourceResolver;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Node;
import com.simbiosystems.cocoon.xmlform.xindice.howto.XindiceManager;
/**
* This action handles XMLForms for the Artist data
*/
public class ArtistAction extends AbstractXMLFormAction implements FormListener {
// different form views participating in the form
final String VIEW_START = "start";
final String VIEW_ARTIST = "artist";
final String VIEW_END = "end";
final String VIEW_ERROR = "error";
// action commands used in the wizard
final String CMD_START = "start";
final String CMD_NEXT = "next";
final String CMD_PREV = "prev";
//constants for the XML manipulation, it holds the full name of the collection
//to be used for storing the data
final String xindiceSubCol = "/Artist";
/**
* The first callback method which is called
* when an action is invoked.
*
* It is called before population.
* @return null if the Action is ready to continue.
* an objectModel map which will be returned
*/
protected Map prepare() {
if ( getCommand() == null ) {
return page(VIEW_START);
}
else if ( getCommand().equals(CMD_START)) {
// reset state by removing old form if one exists
Form.remove( getObjectModel(), getFormId() );
getForm().addFormListener( this );
return page(VIEW_ARTIST);
}
// get ready for action
// if not ready return page("whereNext");
return null;
}
/**
* Invoked after form population
* Take appropriate action based on the command
*
*/
public Map perform () {
// get the model which this Form encapsulates
// and apply logic to the model
ArtistBean jBean=(ArtistBean)getForm().getModel();
// set the page control flow parameter
// according to the validation result
if ( getCommand().equals(CMD_NEXT) &&
getForm().getViolations () != null ) {
// errors, back to the same page
return page( getFormView() );
}
else {
// validation passed
// continue with control flow
// clear validation left overs in case the user
// did not press the Next button
getForm().clearViolations();
// get the user submitted command
String command = getCommand();
// get the form view which was submitted
String formView = getFormView();
// apply control flow rules
if (formView.equals (VIEW_ARTIST)) {
if (command.equals(CMD_NEXT)) {
//extended validation
//test if the ID already exists in the DB
Node artistName = jBean.getArtistDocument().getAttributes().getNamedItem("id");
try {
XindiceManager xi = new XindiceManager();
Node result = xi.find(xindiceSubCol, "//Artist[@id='"+ artistName.getNodeValue() +"']", "Artists");
//if we do not get a null the element with that ID
//already existed we add the violation
if (result!=null) {
Violation v = new Violation();
v.setMessage("already exists in the database, please choose another one");
v.setPath("/artistName");
Violation[] va = { v };
getForm().addViolations(Arrays.asList((Object[])va));
//the ID already exists, back to the same
//page to correct the error
return page(VIEW_ARTIST);
}
}
catch (Exception e) {
getLogger().error("Cannot establish a connection to the DB", e);
}
//everything went fine, add the document to the database
try {
createDocument(jBean);
}
catch(Exception e) {
//there were errors, send it to the error page
getLogger().error("Cannot add DOM document to the database");
return page(VIEW_ERROR);
}
return page( VIEW_END);
}
if (command.equals(CMD_PREV)) {
return page(VIEW_START);
}
}
}
}
/**
* FormListener callback
* called in the beginning Form.populate()
* before population starts.
*
* This is the place to handle unchecked checkboxes.
*
*/
public void reset( Form form ) {
// based on the current form view
// make some decisions regarding checkboxes, etc.
String formView = getFormView();
}
/**
* Inserts the document into the database
*/
public void createDocument(ArtistBean jBean)
throws Exception {
try {
//add the document to the database
XindiceManager xi = new XindiceManager();
xi.add(xindiceSubCol, jBean.getArtistDocument(), null);
}
catch (Exception e) {
getLogger().error("DOM Document could not be created", e);
throw e;
}
}
}
The helper class
In order to make this work we need to use a helper class. This class uses the XMLDB to connect to Xindice and make the operations available to the Action. You should extend it to add more operations. The helper class XindiceManager.javais as follows:
/**
* Helper class for Xindice related operations
*
*/
package com.simbiosystems.cocoon.xmlform.xindice.howto;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Database;
import org.xmldb.api.base.Resource;
import org.xmldb.api.base.ResourceIterator;
import org.xmldb.api.base.ResourceSet;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.XMLResource;
import org.xmldb.api.modules.XPathQueryService;
public class XindiceManager {
private static final String driver = "org.apache.xindice.client.xmldb.DatabaseImpl";
private static final String rootCollection = "xmldb:xindice:///db/";
/**
* Constructor
*
*/
public XindiceManager() {
}
/**
* Search for a document in the DB. If not found, return null.
*
* @param subCol name of the subCollection to query if any. If blank or null,
* queries go against the rootCollection.
* @param xpath XPath expression for the query, if none, it returns the whole Collection
* @param resultRootelement name of the root element for the DOM document which will
* wrap the results
* @return a DOM Node with the matched documents
*
*/
public Node find(String subCol, String xpath, String resultRootElement)
throws Exception {
//prepare DOM document
DOMImplementation impl;
DocumentBuilder builder;
try {
// Find the implementation
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware( false );
factory.setValidating ( false );
builder = factory.newDocumentBuilder();
impl = builder.getDOMImplementation();
}
catch (Exception ex) {
throw new RuntimeException("[XindiceManager.find]: Failed to initialize DOM factory. Root cause: \n" + ex);
}
//create the Document which will hold the results
Document resultDoc = impl.createDocument(null, resultRootElement, null);
Node root = resultDoc.getDocumentElement();
//And now the Xindice part
Collection col = null;
String strData = null;
try {
Class c = Class.forName(driver);
Database database = (Database) c.newInstance();
DatabaseManager.registerDatabase(database);
String localCol = rootCollection + subCol;
col = DatabaseManager.getCollection(localCol);
//Make the XPath query and get the resources
XPathQueryService service =
(XPathQueryService) col.getService("XPathQueryService", "1.0");
ResourceSet resultSet = service.query(xpath);
// Iterate the xpath results and add each of them to the main Document
ResourceIterator iterator = resultSet.getIterator();
//if not resources are present, just return null
if(!iterator.hasMoreResources()) return null;
while(iterator.hasMoreResources()) {
Resource r = iterator.nextResource();
Element resElement = ((Document)((XMLResource)r).getContentAsDOM()).getDocumentElement();
// Remove unwanted attributes
resElement.removeAttribute("src:col");
resElement.removeAttribute("src:key");
resElement.removeAttribute("xmlns:src");
// Add this result to the root element
Element importedElement = (Element)resultDoc.importNode(resElement, true);
root.appendChild(importedElement);
}
//return the Document root
return root;
}
catch (Exception e) {
System.err.println("[XindiceManager.find]: Find Exception occured :" + e.getMessage());
throw e;
}
finally {
if (col != null) {
col.close();
}
}
}
/**
* Add a document to the DB. If a key is not passed, it generates a unique one.
* This version uses a DOM Document.
* @param subCol name of the subCollection in which to add the resource if any. If blank or
* null, insertions go against the rootCollection.
* @param document DOM document to be inserted
* @param key unique key for the resource to be created, if none, one is created on the fly
*/
public void add(String subCol, Node document, String key) throws Exception {
Collection col = null;
Node xmldoc = null;
try {
Class c = Class.forName(driver);
Database database = (Database) c.newInstance();
DatabaseManager.registerDatabase(database);
col = DatabaseManager.getCollection(rootCollection + subCol);
XMLResource document_ = (XMLResource) col.createResource(null, "XMLResource");
document_.setContentAsDOM(document);
col.storeResource(document_);
}
catch (XMLDBException e) {
System.err.println("XML:DB - Add Exception occured " + e.errorCode);
throw e;
}
finally {
if (col != null) {
col.close();
}
}
}
}
The Sitemap
Remember you should have the XMLFormTransformer defined like this:
<map:transformer name="xmlform" src="org.apache.cocoon.transformation.XMLFormTransformer" logger="xmlform"/>
Then, you must declare the Action to be used in the correspondent section:
<map:action logger="xmlform" name="ArtistAction" src="com.simbiosystems.cocoon.xmlform.xindice.howto.ArtistAction"/>
The Pipeline
<!-- XMLForms pipeline -->
<map:pipeline>
<map:match pattern="**/*.xform">
<map:act type="ArtistAction">
<map:parameter name="actionName" value="{2}Action"/>
<!-- XMLForm parameters for the Action -->
<map:parameter name="xmlform-validator-schema-ns" value="http://www.ascc.net/xml/schematron"/>
<map:parameter name="xmlform-validator-schema" value="{1}/{2}/schematron/artist-validator.xml"/>
<map:parameter name="xmlform-id" value="form-insert"/>
<map:parameter name="xmlform-scope" value="session"/>
<map:parameter name="xmlform-model"
value="com.simbiosystems.cocoon.xmlform.xindice.howto.ArtistBean"/>
<!-- XMLForm document, {page} comes from Action -->
<map:generate src="{../1}/{../2}/{page}.xml"/>
</map:act>
<!-- populating the doc with model instance data -->
<map:transform type="xmlform"/>
<!-- look and feel of the form controls -->
<map:transform src="styles/wizard2html.xsl"/>
<!-- Transforming the XMLForm controls to HTML -->
<map:transform src="styles/xmlform2html.xsl"/>
<!-- sending the HTML back to the browser -->
<map:serialize type="html"/>
</map:match>
</map:pipeline>
Depending on where you have Cocoon installed and where you have configured the files in this howto, you could make a request to a URL like http://localhost:8080/cocoon/Artist.xformand start using the Form.
Final Considerations
Making the operations from the Action using the helper class seems to be the easiest way. You can think of other ways or other operations using other Xindice tools available in Cocoon such as the pseudo protocol. For example, you could use it to query the DB for data that could be stored in a sitemap parameter. You could then get the data from the Action and use it to fill a selectbox in the form.\\ We did not mentioned other operations such as updates. This can be also accomplished this way. For example, if you want to edit the data we just stored, you could load it in the DOM Node at the beginning by using the findmethod of the helper class. This way you get a filled form in the next step ready for editing. Other ways or interacting with the repository include the XMLDB Transformer. Since it uses XUpdate alike syntax, you could format a XML String for it in the last step of the Action, making it available to the sitemap then, so the Transformer could get it and make the operations.\\ I'm sure you can think of more different ways. I encourage you to contribute them if you have tested it succesfully.
Summary
This How-To makes possible the use of Xindice from XMLForms in order to add data to the repository. You learned how to connect to the DB from the Action, and how to make complex validation using information stored in the DB. I hope it was helpful for you.
XSPSyntax
Describes XML syntax for XSP
To use XSP tags you must include the XSP namespace. Example:
<xsp:page xmlns:xsp="http://apache.org/xsp"> </xsp:page>
xsp:page
Is the root element of every XSP document. Inside this tag you put all the others tags you will need. The use of this tag is mandatory.
Note that, this is notthe root element of the generated document. During the translation process -- in which the XSP page is turned in source code for a particular language -- all elements in the XSP namespace are replaced with programming language structures, i.e. imports, code blocks, statements, expressions, etc.
All XSP pages will end up generating a subclass of XSPGenerator. See the XSP Environmentfor more details.
The XML Namespaces for all Logicsheets referenced by this XSP page should be declared here, along with the XSP namespace itself. You should avoid using the default namespace on XSP elements as you'll have to redeclare it on all of your own elements should they not be in a namespace of their own.
The page element should have a languageattribute, indicating the programming language used within that page. Therefore, while XSP may be language neutral, each page is tied to a specificlanguage.
The page element may contain several children, but can only have a singlechild which is not in the XSP namespace. This userelement will become the root of the generated document.
All markup within the user element is generated by the XSPGenerator.generate()method.
Children: single user element, xsp:structure, xsp:logic, xsp:init-page
xsp:structure && xsp:include
These elements are used together to allow additional program modules/libraries/classes, required by the code in the rest of the code, to be included in the generated source code.
In Java these elements are used to signify import statements for the generated class. You should include imports for any classes in the Java API, or those from your own class libraries that are required by code in the XSP.
Children: (xsp:structure) xsp:include; (xsp:include) none
xsp:init-page
Is a top level tag. It fired just before the call of the startDocument()function.
This element can be used to execute any user-defined initializations just before the start of the generation of the target document. Example: can be used to define local variables.
Since it is outside the document scope, this tag cannotbe used to output anything to the client like headers or footers. Also that means that all the code put here may be only in the target programming languaje (Java, Javascript or Python).
Children: none
xsp:exit-page
Is a top level tag. It fired just after the call of the endDocument()function.
This element is used to contain any user-defined necessary code after the end of the generation of the target document. Example: can be used to clean up local variables.
Since it is outside the document scope, this tag cannotbe used to output anything to the client like headers or footers. Also that means that all the code put here may be only in the target programming languaje (Java, Javascript or Python).
Children: none
xsp:logic
These elements are used to contain blocks of code. This may be method declarations or just sequences of application logic.
xsp:logic elements that appear outside of the userelement are deemed to be class level methods and declarations -- i.e. static methods, member variables, etc.
xsp:logic elements used elsewhere result in the addition of these code blocks to the generate()method.
To avoid escaping all of the programming code to ensure that it's well-formed, it's possible to wrap the code in CDATA sections. You must ensure however that the sequence ]]> does not occur within the code block -- this is a rare occurence in any rate.
The xsp:logic element is much friendlier than the equivalent <% %> JSP syntax. For example in JSP it's common to see blocks of the form:
<% if (someTest()) { %>
<b>Success!</b>
<% } else { %>
<b>Fail!</b>
<% } %>
In an xsp:logic element this would look like:
<xsp:logic>
if (someTest()) {
<b>Success!</b>
} else {
<b>Fail!</b>
}
</xsp:logic>
There's no need for the additional escaping used in JSP to separate program code from elements designed for the output, as the XML parser can already distinguish between text content and child elements.
The XSLT transform used to create the code can then handle these differently: text contained within an xsp:logic element is treated as Java code, whereas elements not in the XSP namespace are substituted by program code that will fire the required SAX events.
An xsp:logic element can contain xsp:expr elements, xsp:content elements, or elements that will be passed directly to the generated document. Ensure however that the content remains well-formed.
For example the following is not legal:
<search-results>
<xsp:logic>
if (firstResult()) {
<result id="first">
} else {
<result>
}
...result generation code here...
</result>
</xsp:logic>
</search-results>
The above code, which is generating some results, e.g. from a database query or search, and is attempting to treat the first result differently to subsequent results by adding an additional attribute.
However if you attempt to load this XSP page you'll get a 404 Not Found error. Digging into the cocoon.logwill show that an exception has been thrown whilst parsing the XSP page:
org.xml.sax.SAXException: Stopping after fatal error: The element type "result" must be terminated by the matching end-tag "</result>".
One way to handle this would be:
<search-results>
<xsp:logic>
if (firstResult()) {
<result id="first">
... handle first result..
</result>
} else {
<result>
...handle other results...
</result>
}
</xsp:logic>
</search-results>
The file is now well-formed.
xsp:expr
An xsp:expr element is used to signify Java expressions, and is equivalent to the <%= %> syntax in JSP.
The contents of an xsp:expr element is passed directly to the XSPObjectHelper.xspExpr(contentHandler, ...) method. Therefore it's an expressionand not a statement. As it is not a statement there is no need to terminate the text content with a semi-colon.
Can be used within both user elements and other XSP elements.
Children: none
xsp:element
XSLT provides two mechanisms to create elements for output from a transformation. The elements can either be present directly in the stylesheet (literal result elements) or generated dynamically using xsl:element. This allows the precise element to be created to be totally under the control of the stylesheet.
XSP provides a directly equivalent mechanism. We've already seen that user elements can be added to the XSP page and these are sent directly to the output. These are the equivalent to XSL literal result elements. Dynamically created elements can be produced using the xsp:element.
The name of the dynamically created element should be defined by including an xsp:param child of this element. This xsp:param element must have the value of it's name attribute be "name". The contents of this element must be an xsp:expr element which contains the code that will generate the element name, or "someString" (including quotes).
A namespace can be assigned by including two additional parameters with the name "uri" and "prefix". It is an error to only provide one of these.
Children: xsp:param, xsp:attribute.
xsp:attribute
Used to create an attribute. Similar to xsl:attribute.
Should contain an xsp:param element whose name is "name". This indicates the name of the element. A namespace can be assigned by including two additional parameters with the name "uri" and "prefix". It is an error to only provide one of these.
Children: xsp:param, xsp:expr
xsp:content
Adds character content to the output. Contents is interpreted as a string. If an element is added as a child then it is passed through as a literal result element.
xsp:pi
Used to create a processing instruction.
Should contain an xsp:param element whose name is "target". This indicates the target of the processing instruction. The content can described using the content of the xsp:pi element, or nested xsp:expr elements.
These are concatenated to form the final content.
xsp:comment
Used to create a comment inside the generated source code.
xsp:param
Used to describe parameters.
Should have a name attribute indicating the name of the parameter.
index
Wiki Samples
This directory contains a selection of Wiki files from the Cocoon Wiki. Click on the links in the left menu to view each Wiki file. If you have a fast connection, try the aggregated HTML(442k) or aggregated PDF(582k).


