This documentation relates to the latest Pustefix release from the 0.12 branch.
Copyright © 2007, 2008, 2009, 2010 1&1 Internet AG
Table of Contents
List of Tables
Table of Contents
Some parts of the Pustefix frameworks are configured using Java properties. To ease this configuration Pustefix provides you with a special XML format which is read instead of the usual Java property file format. This format provides some customization mechanism to allow configuration options to depend on settings like the makemode or the machine the application is being built on.
The structure of a standard .xml property file is very easy:
<standardprops version="1.0" xmlns="http://pustefix.sourceforge.net/2004/properties" xmlns:cus="http://www.schlund.de/pustefix/customize" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pustefix.sourceforge.net/2004/properties http://pustefix.sourceforge.net/2004/properties.xsd">
The cus namespace is needed to use
the customization tags explained below.
<properties> <prop name="statuscodefactory.messagefile">common/dyntxt/statusmessages.xml</prop>
The prop tag is the most primitive way to enter a single property.
The example above would simply result in the java property
statuscodefactory.messagefile=common/dyntxt/statusmessages.xml.
</properties> </standardprops>
Pustefix allows to customize the creation of the property files in certain ways. When the file is being read, the system uses settings defined in common/conf/buildtime.prop (which itself is automatically generated by the build system) to provide the parameters used in the customization tags:
makemode The value of the ant property
makemode, which can be passed on the ant commandline but will
more commonly be specified in the environment variable
MAKE_MODE. This can be used to easily switch preferences
depending on the "mode" that is active when the application
is built, e.g. "test" on development machines, and "prod" on
the production servers. The value is supplied as the parameter
mode. Note that the value of MAKE_MODE also determines if
certain features are allowed like the possiblity to use the
Pustefix editor or to get the XML result instead of the
rendered page. These features are turned off for security
reasons when the mode is "prod".
The user id (uid) of the user that starts
the build process.
The full qualified domainname (as the parameter
fqdn) of the machine you start the build
process.
The name of the machine (machine) you
start the build process on.
Any ant property (__antprop_*) defined
within the build file.
Certain customization tags work with these parameters:
<properties xmlns:cus="http://www.schlund.de/pustefix/customize"> <prop name="foo"> <cus:uid/> </prop>
Use cus:uid to reference the user that started
the build process. In the same way you can use
cus:machine and cus:fqdn to insert
the value of the supplied XSLT parameter
<choose> <when test="$mode='test'"> </when> <when test="$uid='pfixuser'"> </when> <when test="$machine='server1'">
These choose and when elements are
allowed only within properties elements. The three
test nodes above test for one
of the supplied XSLT parameters...
</when>
<when test="$mode='prod' and $uid='pfixuser'">
</when>
<when test="$mode='prod' or machine='server1'">
...you can use boolean operators to combine tests
</when>
<otherwise>
The "default test" that will always pass.
</otherwise>
</choose>
</properties>
If you think the customization system really looks like XSLT, you are almost right as in fact the test expressions are coded using XPath - the same standard XSLT is using, too.
This customization mechanism is not only available within the XML property files but also in most other Pustefix configuration files.
Most projects use one or a (small) number of
de.schlund.pfixxml.ContextXMLServlet
servlets that drive the business logic of the application.
All pages that are to be generated via XSLT from a data XML
tree are handled by a servlet of this type.
This servlet uses a configuration file that has a special syntax. However properties and customization in this file work nearly the same way as explained for the standard property definitions.
<contextxmlserver version="1.0" xmlns="http://pustefix.sourceforge.net/2004/properties" xmlns:cus="http://www.schlund.de/pustefix/customize" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pustefix.sourceforge.net/2004/properties http://pustefix.sourceforge.net/2004/properties.xsd"> <servletinfo depend="MyProject/conf/depend.xml" name="AUniqueNameForTheServlet">
The attributes depend and
name are mandatory. If
depend starts with a '/'
the value is used unchanged, otherwise (which is the
usual case) it's interpreted as a path relative to
the docroot.
<editmode allow="true|false"/>
allow needs to be set to
true if you want to be able to use
the pustefix editor. You can wrap this tag within a
customization element to allow edit mode only in
certain modes (e.g. test mode). The whole node is
optional.
<ssl force="true|false" modes="ListOfModes"/>
force must be set to
true if the servlet needs to run
under SSL. You can wrap this tag within a
customization element to force use of SSL only in
certain modes (e.g. prod mode). The whole node is
optional.
<defaultstate class="a.state.Class"/>
<defaultihandlerstate class="another.state.Class"/>
defaultstate and
defaultihandlerstate are both optional.
The class attribute must
be given. a.state.Class should
de a descendant of
de.schlund.pfixcore.workflow.app.StaticState
and another.state.Class should
be a descendant of
de.schlund.pfixcore.workflow.app.DefaultIWrapperState
(unless you really know what you are doing). They are
used to set the defaults for the state tag used when
processing the pagerequest tag
(see there for more info).
</servletinfo> <context defaultflow="AFlowName" authpage="APageName" synchronized="true">
| Attribute | Description |
|---|---|
defaultflow |
Mandatory. Must reference a
valid pageflow (See below). |
authpage |
Optional. It must be given, if the servlet should
supply a login/authentification page. If given, it must
reference a valid pagerequest with an
auth child node. |
synchronized |
Optional. Defaults to true. If
set to true, only one request per
session is handled concurrently. If set to
false all requests will be handled
concurrently, requiring thread-safe business logic. |
<resource class="AContextResource">
class is
mandatory, must be a
ContextResource. There may be multiple
resource tags given.
<implements class="An_Interface">
class is
mandatory, must be an interface
implemented by the ContextResource.
There may be more than one implements tag for a
ContextResource, but each interface
must be unique in the whole context. In other words: it's
possible for a ContextResource to
implement more than one interface, but not possible for one
interface to be implemented by two
ContextResources used in the same
Context definition.
</implements>
<properties>
The whole node is optional.
<prop name="AName">AValue</prop>
prop is mandatory and can
be used multiple times. It's similar to the use as a child
of pagerequest/properties, but used
here to create properties that are related to a context
resource implementation. The resulting property looks like this:
context.resourceparameter.AContextResource.AName=AValue
Customization tags may be used around a property to make it
depend on a certain makemode, etc.
</properties>
</resource>
</context>
<scriptedflow name="AName" file="path/to/scriptfile.xml"/>
There may be an arbitrary number of scriptedflow
tags, but each one must have a unique name. Scripted flows
are a special method to control a session and do automatic
requests based on initial user input.
<role name="MY_ROLE"> <pageaccess names="mypage*"/> </role>
You can define an arbitrary number of roles and assigned authorizations here, for details see Role-based authorization.
<pageflow name="AName" final="APageName" stopnext="true|false">
There may be multiple pageflow tags defined, but you need at least one (which must be referenced by the defaultflow attribute above). We only describe the normal case without using variants. See here for more information on how to handle variants of pageflows.
| Attribute | Description |
|---|---|
name |
Mandatory. Must be a unique name. |
final |
Optional, must reference a page with a valid
pagerequest definition given in this property
file. There may be many pageflows defined for a servlet.
A page may well be used in more than one pageflow. |
stopnext |
Optional, defaults to false. If given and
true, the pageflow will stop at the
next accessible page after the current page even if this
page would normally be skipped in the workflow because
it doesn't need any input. |
<flowstep name="AnotherPageName" stophere="true|false">
| Attribute | Description |
|---|---|
name |
Mandatory. Must reference a
valid pagerequest. Usually there are many
flowsteps defined in a pageflow. |
stophere |
Optional, if true the pageflow will stop at this
step unconditionally if the submit originated from a
step that's before this one in the pageflow. See
also the stopnext
attribute of the tag which is quivalent to
specifying stophere="true" for
every single flowstep. |
<oncontinue applyall="true|false">
This tag (which is optional) starts a sequence of
test/action pairs. The tests are XPath expressions which
work on the DOM tree as produced by the flowstep's
associated state (note that the navigation is not
inserted into the DOM tree at this stage, and the
/formresult/formvalues and
/formresult/formerrors paths are also
not present). The pageflow system calls the tests whenever
a state returns a ResultDocument (before it continues with
other stuff e.g. a pageflow run).
The applyall attribute is
optional. If given and true, all actions
with matching conditions are executed, if not given or
false (the default) only the first
action with a matching condition is executed.
<when test="A_XPath_Expression">
The when tag contains the XPath expression to try
in it's test attribute. If this
attribute is omitted, the whole condition is considered to
be true.
<action type="jumpto" page="APage" pageflow="APageFlow">
The action tag denotes the
FlowStepAction to execute. The
type attribute is mandatory and
defines the special action to use. The string
jumpto denotes the special
FlowStepAction
de.schlund.pfixcore.workflow.FlowStepJumpToAction
which is used to set the jumptopage (defined via the
page attribute) and/or the
jumptopageflow (defined via the
pageflow attribute).
</action>
</when>
<when test="A_XPath_Expression">
<action type="A_FlowStepAction" somekey="somevalue">
If the type attribute is not
jumpto, the value is interpreted as a
class of type
de.schlund.pfixcore.workflow.FlowStepAction.
There can be an arbitrary number of additional
attributes (somekey in this example) which are
supplied as named parameters to the special
FlowStepAction.
</action>
</when>
</oncontinue>
</flowstep>
</pageflow>
<pagerequest name="APageName" nostore="true|false" copyfrom="APageName">
| Attribute | Description |
|---|---|
name |
Mandatory. It must be the name of a page defined in the corresponding depend.xml file. |
nostore |
Optional. If given, and set to true
makes the page a so called sidestep page. This has the
consequence that the resulting DOM tree is not stored in
the session (so no frames are possible!) and that the
status of the Context object
(current page, current pageflow) isn't changed. The use of
this feature is to allow for pages to act as e.g. CSS or
JavaScript without disturbing the flow of the main pages.
See also the section about properties below. |
copyfrom |
Optional. If given, and set to the name of a valid
pagerequest, all configuration from this
referenced pagerequest are used for the current
page, disregarding all configuration that is made in this
pagerequest. It's a plain and simple copy, no
extending, no restricting! |
<ssl force="true|false"/>
The node is optional. If given, and the attribute
force is set to
true, the page will only run under SSL when
jumped to via a link or a submit of form data. If the session
currently does not run under SSL, the system will make sure to
redirect to a secure session prior to handling the request.
After a session is running under SSL, there is no way back
(so all other pages will run securely regardless if they have
a ssl node or not).
You can wrap this tag within a customization element to force
use of SSL only in certain modes (e.g. prod
mode).
![]() | Note |
|---|---|
You can force the servlet as a whole to run only under SSL by specifying the ssl subnode of the servletinfo node. |
<state class="AClassName"/>
The whole node is optional. If given, the
class attribute must be the name
of a java class implementing the
de.schlund.pfixcore.workflow.State
interface. The used State is determined
as follows:
If state is given, use the value of it's
class attribute. Else:
If the pagerequest has a auth
child, use
de.schlund.pfixcore.workflow.app.DefaultAuthIWrapperState.
Else:
If the pagerequest has an input child, use
the value of the class
attribute of the defaultihandlerstate tag
explained above if it is given. If this is not given, just
use de.schlund.pfixcore.workflow.app.DefaultIWrapperState.
Else:
use the value of the class
attribute of the defaultstate tag explained above
if it is given. If this is not given, just use
de.schlund.pfixcore.workflow.app.StaticState.
You can use any BSF-supported scripting language for writing your State-implementation, too. See PustefixScripting for details on how to do this.
<finalizer class="AClassName"/>
The whole node is optional. It may only be given for a
State that is either
de.schlund.pfixcore.workflow.app.DefaultIWrapperState
or a descendent of it.
The class attribute is mandatory
and denotes a class implementing
de.schlund.pfixcore.workflow.app.ResdocFinalizer.
![]() | Caution |
|---|---|
The use of finalizers is not suggested most of the time! They can completely change the result document and the logic when to trigger the next step in the current page flow. Use them at your own risk. Or better: Don't use them at all. |
<input policy="ANY|ALL|NONE">
The whole node is optional. It may only be given for a
State that is either
de.schlund.pfixcore.workflow.app.DefaultIWrapperState
or a descendent of it!
policy is optional (default is
ANY). The policy decides when a whole page is
considered to be accessible:
ANY: just one of the associated handlers needs to be active for the page to be accessible.
ALL: all the associated handlers must be active for the page to be accessible.
NONE: none of the associated handlers needs to be active for the page to be accessible.
If one of the associated handlers returns
false on calling
prerequisitesMet(), the page is of
course still inaccessible.
<interface prefix="AName" class="AClassName" activeignore="true|false" continue="true|false" alwaysretrieve="true|false"/>
There can be many interface nodes for a page. Each
one references an "atomic" functional entity consisting of an
IWrapper java class (usually
autogenerated from a .iwrp xml file, see
here) that defines the type and names of the parameters passed
between the UI and the functional entity and an associated
IHandler java class that uses the
IWrapper to retrieve the passed
parameters via typed getter methods.
| Attribute | Description |
|---|---|
prefix |
Mandatory. The prefix defines a
name for the IWrapper and in effect
a namespace for the IWrapper's
parameters. If the prefix "bar" is
defined for an IWrapper that
contains a parameter called "Foo", the
submitted HTTP parameter must be called
bar.Foo. |
class |
Mandatory. Must be the name of a
java class implementing
de.schlund.pfixcore.generator.IWrapper.
This implicitly defines a
de.schlund.pfixcore.generator.IHandler,
as every IWrapper knows it's
associated IHandler and can be
queried for it. |
continue |
Optional, defaults to false.
If the user submits data usually all defined handlers are
called. If the submit doesn't result in errors, the usual
action is that the context will switch to the next page
according to the current pageflow.
This is different when the submit is restricted to a
subset of one ore more of the page's defined handlers. The
usual action in this case is to stay on the page no matter
if the data submit was successful (no errors happened) or
not. BUT: if ALL
of the handlers the submit was restricted to have the
attribute continue set to
true, the context again will switch to
the next page. |
activeignore |
Optional, default is false. The
IHandler method
isActive() is
NOT called on handlers with
activeignore set to
true. In other words: the handler is
ignored when the system tries to find out if the page is
accessible or not. See also the comment for the
policy attribute above. |
alwaysretrieve |
Optional, default is false. This
attribute changes the way the not selected
IHandlers of a page are treated
when the system decides that it should stay on the page
after a submit has been handled without an error.
The default behaviour is to only call
retrieveCurrentStatus() on those
IHandlers that are in the set of
selected (via the SELWRP command)
IHandlers. For the remaining
IHandlers, only the (ignored) input
is copied back verbatim so the net effect is that all
associated form elements stay the same as they were before
the submit.
By setting alwaysretrieve to
true the
retrieveCurrentStatus() method of
this IHandler is also called in the
described situation. You need this if a submit handled in
one or more IHandler(s) should
change the filled-in form values of another, not currently
selected IHandler. |
</input>
![]() | Caution |
|---|---|
Only one of |
<auth>
The whole node is optional. It may only be given for a page
using a State that is either
de.schlund.pfixcore.workflow.app.DefaultAuthIWrapperState
or a descendent of it!
There may be only one pagerequest using such a
State, it must be the one that is also
referred to via the authpage
attribute of the context node above. Other
pagerequest nodes must not have this subnode!
<authinterface prefix="AName" class="AClassName"/>
<auxinterface prefix="AName" class="AClassName"/>
One and only one authinterface node must be given.
auxinterface nodes are optional and can be given
multiple times.
The prefix and
class attributes are mandatory for
both nodes. The meaning is the same as for the
interface subnodes of the ihandler node.
The authinterface node denotes the one
IWrapper/IHandler
combo that is responsible for checking the authentification of a
user session. The auxinterface nodes denote
additional IWrappers/IHandlers
that are called after the authentification handler if the
authorization is granted.
Note that no error handling whatsoever takes place for
auxinterface IHandlers.
</auth>
<output>
The whole node is optional. Every page using a
State that is itself or a descendant of
de.schlund.pfixcore.workflow.app.StaticState
can use this.
You can have as many resource childnodes as you like.
<resource node="AName" class="AClassName"/>
| Attribute | Description |
|---|---|
class |
Mandatory.
class is one of the
ContextResources defined via
implements above. |
node |
Mandatory.
node is the node in the
output tree ("/formresult/AName") under
which the ContextResource inserts
it's data. |
</output>
<properties>
The whole node is optional.
<prop name="APropertyKey">AValue</prop>
The node is mandatory and can be used multiple times. It will be
transformed into a java property that is associated to the page.
There are some props that are already defined for
de.schlund.pfixcore.workflow.app.StaticState
and descendants. These are listed below
| Property Name | Property Value | Description |
|---|---|---|
mimetype |
e.g. text/css |
If given, sets the mimetype of the HttpResponse
object to something else than the default
text/html. This is most often used for
text/css. |
responseheader.A_HEADER |
A_VALUE |
If given, set the header A_HEADER
of the HttpResponse object to A_VALUE.
NOTE: the Pustefix system uses a set of default headers
that are only used, when no user defined headers are
given! The set of default headers is:
Expires=Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control=private
If you want to use some of them in addition to your own
headers, you must manually supply them, too. |
</properties>
</pagerequest>
<config-include file="myproject/conf/myfile.xml" section="pagerequests"/>
Includes a part of a config fragments file at this location. See Section 1.4, “Configuration fragments file format” for details on how to define config fragments.
| Attribute | Description |
|---|---|
file |
Mandatory. Path to the file that contains the tags to be included (relative to docroot). |
section |
Optional. Type of the section that shall be included. If more than one section of the specified type exists in the file, the content of all this sections is included. |
refid |
Optional. Include a section identified by the specified id. The refid specified here must match the id attribute of exactly one section in the specified file. |
xpath |
Optional.
A XPath expression specifying the node-set to be included.
The prefixes to be used for XML namespaces are "fr" for
the namespace of the
fragments file
tags and "pr" for the namespace of the
ContextXMLServlet configuration
tags. |
One and only one of the section, refid or xpath attribute has to be specified for each config-include.
<properties> <prop name="AProperty">AValue</prop>
| Property Name | Property Value | Description |
|---|---|---|
mimetype |
e.g. text/css |
If given, sets the mimetype of the HttpResponse
object to something else than the default
text/html. This is most often used for
text/css. |
responseheader.A_HEADER |
A_VALUE |
If given, set the header A_HEADER
of the HttpResponse object to A_VALUE.
Headers set here can be overwritten for specific pages.
NOTE: the Pustefix system uses a set of default headers
that are only used, when no user defined headers are
given! The set of default headers is:
Expires=Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control=private
If you want to use some of them in addition to your own
headers, you must manually supply them, too. |
You can also specify properties here that are understood by the ServletManager class.
</properties> </contextxmlserver>
Occasionally you don't want to generate output with an XSLT
Transformation, but e.g. deliver binary content directly to the
output stream instead. In this case you need to provide another
servlet of type
de.schlund.pfixxml.DirectOutputServlet.
This servlet doesn't have it's own Context
object but instead "hijacks" the Context
of a foreign
de.schlund.pfixxml.ContextXMLServlet. The
context isn't used for any pageflow handling, only as a means to
get to the data (via its ContextResources)
of the "main" application.
The servlet knows about one or many
directoutputpagerequests. For the XML/XSLT
side of things, they look like normal pages (in fact, the value
of the directoutputpagerequest's name
attribute must be a page defined in
depened.xml. Of course, no target
definition has to be given, only the page in the navigation
structure must exist). But other than the usual
pagerequest, a
directoutputpagerequest has an associated
directoutputstate whose class attribute is a
java class implementing
de.schlund.pfixcore.workflow.app.DirectOutputState.
<directoutputserver version="1.0" xmlns="http://pustefix.sourceforge.net/2004/properties" xmlns:cus="http://www.schlund.de/pustefix/customize" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pustefix.sourceforge.net/2004/properties http://pustefix.sourceforge.net/2004/properties.xsd"> <directoutputservletinfo depend="MyProject/conf/depend.xml" name="AUniqueNameForTheServlet"> <ssl force="true|false"/> <editmode allow="true|false"/>
See the comment for the servletinfo node in
Section 1.2, “ContextXMLServlet configuration format”.
</directoutputservletinfo> <foreigncontext externalservletname="UniqueNameOfAnotherServlet" synchronized="true"/>
The externalservletname must
reference the name of a servlet of type
de.schlund.pfixxml.ContextXMLServlet.
The Context (or better: the
ContextResourceManager) of this servlet
is used for getting stored application data.
Additionally, if the ContextXMLServlet
requires authentication, it's authentication page is checked
before any DirectOutputState is called.
The synchronized attribute is
optional. It defaults to true. If set to
true, only one request per session is
handled concurrently. If set to false all
requests will be handled concurrently, requiring thread-safe
business logic.
<config-include file="myproject/conf/myfile.xml" section="directoutputpagerequests"/>
Includes a part of a config-fragments at this location. See Section 1.4, “Configuration fragments file format” for details on how to define config fragments.
| Attribute | Description |
|---|---|
file |
Mandatory. Path to the file that contains the tags to be included (relative to docroot). |
section |
Optional.
Type of the section that shall be included. If more than
one section of the specified type exists in the file, the
content of all this sections is included. For a
DirectOutputServlet configuration
only directoutputpagerequests and
properties are valid. |
refid |
Optional. Include a section identified by the specified id. The refid specified here must match the id attribute of exactly one section in the specified file. |
xpath |
Optional.
A XPath expression specifying the node-set to be included.
The prefixes to be used for XML namespaces are "fr" for
the namespace of the fragments file tags and "pr" for the
namespace of the DirectOutputServlet
configuration tags. |
One and only one of the section, refid or xpath attribute has to be specified for each config-include.
<directoutputpagerequest name="APageName"> <directoutputstate class="AClassName"/> <properties>
The whole properties node is optional.
<prop name="APropertyKey">AValue</prop>
The node is mandatory and can be used multiple times. It will be
transformed into a java property that is associated to the page.
The java property that is constructed will look like this:
pagerequest.APpageName.APropertyKey=AValue
where APageName is the value of the
name attribute.
</properties>
</directoutputpagerequest>
</directoutputserver>
Configuration fragments files contain aggregated configuration directives that are intended to be reused in different configuration files.
<fr:config-fragments xmlns:fr="http://pustefix.sourceforge.net/configfragments200609" xmlns:pr="http://pustefix.sourceforge.net/properties200401" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pustefix.sourceforge.net/configfragments200609 http://pustefix.sourceforge.net/configfragments200609.xsd"> <fr:navigation id="nav1">
All sections have an optional
id that can be used to identifiy
the section when more than one section fo the same type is
present in one file. The value of the
id attribute has to be unique
within the whole file.
<page name="MyPage" handler="/xml/myhandler"/>
The structure here is the same as within the navigation tag of the depend.xml file.
</fr:navigation> <fr:targets> <standardpage name="MyPage" xml="myproject/xml/mymaster.xml"/>
The tags allowed here are the same that are allowed for standardpage or target definitions in the depend.xml file.
</fr:targets> <fr:resources> <pr:resource class="com.example.MyResourceImpl"> <pr:implements class="com.example.MyResource"/> </pr:resource>
The tags allowed here are the same that are allowed for the
definition of context resources within the context
tag of the ContextXMLServlet configuration.
</fr:resources> <fr:interceptors> <pr:interceptor class="com.example.MyInterceptor"/>
The tags allowed here are the same that are allowed within the
startinterceptors and endinterceptors tags
of the ContextXMLServlet configuration.
</fr:interceptors> <fr:scriptedflows> <pr:scriptedflow name="myscript" file="myproject/conf/scriptedflows/myscript.script.xml"/>
The tags allowed here are the same that are allowed within the
scriptedflows tag of the
ContextXMLServlet configuration.
</fr:scriptedflows> <fr:roles> <pr:role name="MY_ROLE"> <pr:pageaccess names="mypage*"/> </pr:role>
The tags allowed here are the same that are used for role definition in the ContextXMLServlet configuration.
</fr:roles> <fr:pageflows> <pr:pageflow name="MyFlow"> <pr:flowstep name="MyFirstPage"/> <pr:flowstep name="MySecondPage"/> </pr:pageflow>
The tags allowed here are the same that are used for the definition of pageflows in the ContextXMLServlet configuration.
</fr:pageflows> <fr:pagerequests> <pr:pagerequest name="MyPage"/>
The tags allowed here are the same that are used for the definition of pagerequets in the ContextXMLServlet configuration.
</fr:pagerequests> <fr:properties> <pr:prop name="myproperty">myvalue</pr:prop>
The tags allowed here are the same that are allowed within the
properties tag of the
ContextXMLServlet configuration.
</fr:properties> <fr:directoutputpagerequests> <pr:directoutputpagerequest name="foo">...</pr:directoutputpagerequest>
Direct output pagerequests can be defined here. See Section 1.3, “DirectOutputServlet” for details on this.
</fr:directoutputpagerequests> </fr:config-fragments>
This property file is located in
projects/common/conf/factory.xml and used
by the de.schlund.pfixxml.FactoryInitServlet
servlet (contained in the special admin project used in every
Pustefix environment) whenever the servletcontainer starts up to
initialize services that are used by all the other projects. The
syntax of this file is described in Section 1.1, “Java property files in Pustefix”.
The syntax of the
common/conf/pustefix.xml file
complies to the description in
Section 1.1, “Java property files in Pustefix”. The properties
defined here are merged with the properties defined for a
specific servlet. However, there are some properties with a
special meaning.
Exception processing is configured via prop elements
whose names comply to following syntax:
exception.TYPE.[page|forward|processor]
For one single TYPE, there may be only one
occurrence of page, forward
and processor.
TYPE is a fully qualified class name of a
valid exception class, for which the handling should be
configured at this point. In this case it specifically means,
that the specified class must be a descendant of
java.lang.Throwable, as the
catch-block that handles the exceptions which
are specified here, catches Throwable.
If an exception occurs during exception processing, or during processing of the page the request got forwarded to, no further exception handling will take place. Therefore the code that processes exceptions and the code that drives pages to which requests get forwarded, in case of exceptions, should be robust. Otherwise the whole exception-handling thing would be quite useless, wouldn't it?
| Attribute | Description |
|---|---|
file |
Mandatory. Path to the file that contains the tags to be included (relative to docroot). |
section |
Optional.
Type of the section that shall be included. If more than
one section of the specified type exists in the file, the
content of all this sections is included. For a
DirectOutputServlet configuration
only directoutputpagerequests and
properties are valid. |
refid |
Optional. Include a section identified by the specified id. The refid specified here must match the id attribute of exactly one section in the specified file. |
xpath |
Optional.
A XPath expression specifying the node-set to be included.
The prefixes to be used for XML namespaces are "fr" for
the namespace of the fragments file tags and "pr" for the
namespace of the DirectOutputServlet
configuration tags. |
Table of Contents
Pustefix provides a role-based authorization mechanism. You can define arbitrary roles,
declare logical operations/combinations on this roles using authconstraints,
and assign these authconstraints to pagerequests.
A role is defined using an according XML element with a unique
name attribute value. Setting the initial
attribute to true the role will be automatically
set on context initialization.
<role name="MYROLE" initial="true"/>
Authconstraints can combine various authorization conditions,
supported conditions are: hasrole, and,
or and not, represented by according XML elements.
Using the authpage attribute you can define the page, which should be
called on authorization failure. Using the default attribute you can
set one toplevel authconstraint to be the default one for
all pagerequests having no authconstraint asssigned.
<authconstraint id="MYCONSTRAINT" authpage="login" default="true"> <or> <hasrole name="MYROLE"/> <hasrole name="OTHERROLE"/> </or> </authconstraint>
Pagerequests can either define new authconstraints as
child elements or can reference existing toplevel authconstraints by
their id.
<pagerequest name="mypage"> <authconstraint ref="MYCONSTRAINT"/> ... </pagerequest> <pagerequest name="mypage"> <authconstraint authpage="login"> <hasrole name="MYROLE"/> </authconstraint> ... </pagerequest>
You can programmatically set/query roles using the de.schlund.pfixcore.auth.Authentication
object, which can be retrieved from the Context calling its getAuthentication()
method.
public interface Authentication { public boolean hasRole(String roleName); public boolean addRole(String roleName); public boolean revokeRole(String roleName); public Role[] getRoles(); }
You can add a new role using addRole(), revoke exisiting
roles using revokeRole() or check for a role using
hasRole(). Using getRoles() you get an array
of all currently set roles. If a default role is defined, this role will be initially set.
You can also query the current roles from within your XML/XSLT code using the
XPath extension function pfx:hasRole(rolename)
<ixsl:if test="pfx:hasRole('MYROLE')">
...
</ixsl:if>
If you try to access a page for which the authconstraint isn't fulfilled, you're forwarded
to the according login page. The login page has to be a regular page, i.e. IWrappers on the
page are configured the regular way (no authinterface elements).
Login forms require the type attribute set to roleauth
(instead of auth in the traditional login forms).
<pfx:forminput type="roleauth">
...
</pfx:forminput>
The framework automatically inserts an authentication element into
the login page's DOM tree. This element contains the state of the authenticated
flag, the targetpage which should be accessed, the current roles
and the authorizationfailure containing the violated
authconstraint.
<formresult>
<authentication authenticated="true" targetpage="mypage">
<roles>
<role name="SOMEROLE"/>
</roles>
<authorizationfailure authorization="pageaccess" target="mypage">
<authconstraint>
<hasrole name="MYROLE"/>
</authconstraint>
</authorizationfailure>
</authentication>
...
</formresult>
The following example defines three roles. The role
ANONYMOUS is configured as initial role,
i.e. every session/context has this role automatically set from the beginning.
There are two top-level authconstraints. The authconstraint
AC_DEFAULT is declared as default, i.e. pages, having no explicitly set
authconstraint, will get this one. The authconstraint's authpage
is set to login and it has a simple condition saying that it requires the role
ANONYMOUS.
The authconstraint AC_KNOWN declares that
it requires the USER or the ADMIN role. This
authconstraint is referenced by the pagerequest userpage, using
an empty authconstraint element having a ref attribute containing the
authconstraint's id.
The pagerequest adminpage contains an anonymous
authconstraint element, which defines the role
ADMIN as requirement.
<contextxmlserver> <role name="ANONYMOUS" initial="true"/> <role name="USER"/> <role name="ADMIN"/> <authconstraint id="AC_DEFAULT" authpage="login" default="true"> <hasrole name="ANONYMOUS"/> </authconstraint> <authconstraint id="AC_KNOWN" authpage="login"> <or> <hasrole name="USER"/> <hasrole name="ADMIN"/> </or> </authconstraint> <pagerequest name="home"> ... </pagerequest> <pagerequest name="login"> <input> ... </input> </pagerequest> <pagerequest name="adminpage"> <authconstraint authpage="login"> <hasrole name="ADMIN"/> </authconstraint> ... </pagerequest> <pagerequest name="userpage"> <authconstraint ref="AC_KNOWN"/> ... </pagerequest> ... </contextxmlserver>
Pustefix provides a lightweight object serialization mechanism, which can be used to serialize arbitrary objects into the result DOM without having to do any DOM operations by yourself. The XML binding is customizable via Java annotations within the bean classes.
The framework supports arbitrary Beans, Arrays,
Collections, Maps, Numbers (including
the primitive types and their object wrapper types), Strings,
and Date/Calendar. To support other types or to serialize
to a custom format, it's possible to write your own serializers and annotations (to attach
them to the according bean properties).
The serialization of beans can be customized using the generic Pustefix bean annotations,
which are known from the JSON serialization framework. You can exclude individual properties
from serialization by marking the according getter with an @Exclude
annotation or you can exclude all properties by marking the bean class with an
@ExcludeByDefault annotation and include individual properties with
@Include annotations at their getters (marking public members is supported
too). Using the @Alias annotation you can control
the name used as the resulting attribute or element name.
The serialization to the result tree is done by calling one of the static addObject
methods of the ResultDocument class. The element
argument is the parent DOM element at which the serialized XML will be appended, the optional name
argument can be used to create an additional child element for the serialized XML. The
object argument is the object, which should be serialized.
public class ResultDocument { ... public static Element addObject(Element element, Object object) {...} public static Element addObject(Element element, String name, Object object) {...} }
The default serialization process tries to produce relatively compact XML. Thus it favours attributes over elements and serializes so-called simple types, which can be represented as strings, into attributes where it's possible and makes sense, e.g. for bean properties.
Let's look at an example, which shows the serialization of a simple bean using bean and serializer annotations to customize the serialization behaviour:
...
public class Account {
private long accountNo;
private float debit;
...
public long getAccountNo() {
return accountNo;
}
public void setAccountNo(long accountNo) {
this.accountNo = accountNo;
}
@Alias("balance")
public float getDebit() {
return debit;
}
public void setDebit(float debit) {
this.debit = debit;
}
public Currency getCurrency() {
return currency;
}
public void setCurrency(Currency currency) {
this.currency = currency;
}
@DateSerializer("yyyy-MM-dd HH:mm:ss")
public Calendar getOpeningDate() {
return openingDate;
}
public void setOpeningDate(Calendar openingDate) {
this.openingDate = openingDate;
}
@Exclude
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
}
Here you see how the bean's serialized to the ResultDocument within
a ContextResource:
...
public class ContextAccountImpl implements ContextAccount {
private Account account;
...
public void insertStatus(ResultDocument resdoc, Element elem) throws Exception {
ResultDocument.addObject(elem,"account",account);
}
}
The resulting DOM fragment looks like this:
<formresult serial="1199439160721">
...
<data>
<account accountNo="2000123" currency="EUR" balance="332.54" openingDate="2003-11-04 09:15:38"/>
</data>
...
</formresult>
The data element is the ContextResource's root node as configured
in the configuration file. Calling addObject with the additional
account argument, the serialized bean isn't added directly to the
data element, but an additional element is used. The bean's properties are serialized
as attributes of this element.
The debit property is renamed to balance using
the @Alias annotation. The comment property is
excluded using the @Exclude annotation. The openingDate
property is serialized using the built-in DateSerializer, which can
be customized using the @DateSerializer annotation. Thus you can
provide your own date format pattern (must be a pattern supported by
java.text.SimpleDateFormat).
Only simple type properties, i.e. properties which can be serialized to string values, can
be represented as attributes. If the Account bean would have an additional
property customer of a bean type, e.g. a Customer class,
this property would be serialized as a child element:
<formresult serial="1199439160721">
...
<data>
<account accountNo="2000123" balance="EUR" debit="332.54" openingDate="2003-11-04 09:15:38">
<customer customerId="100000" firstName="Mike" lastName="Foo"/>
</account>
</data>
...
</formresult>
Collections and Arrays are represented using
an element for each entry. The element name is derived from the the simple name
of the entry's class (without package name and starting lowercase):
<formresult serial="1199439160721">
...
<data>
<account accountNo="2000000" currency="EUR" balance="3124.49" openingDate="2003-10-23 08:05:10"/>
<account accountNo="2000123" currency="EUR" balance="332.54" openingDate="2003-11-04 09:15:38"/>
<account accountNo="2001405" currency="EUR" balance="25123.11" openingDate="2005-01-13 10:10:10"/>
</data>
...
</formresult>
The element name can be changed using the @ClassNameAlias annotation, e.g.
to rename the account element to bankaccount:
@ClassNameAlias("bankaccount")
public class Account {
...
}
Maps are represented using an entry element for each
map entry. Key and value are represented by child elements (whereas the element names are
derived from the class names):
<formresult serial="1199439160721">
...
<data>
<entry>
<long>2000000</long>
<account accountNo="2000000" currency="EUR" debit="3124.49" openingDate="2003-10-23 08:05:34"/>
</entry>
<entry>
<key>2001405</key>
<account accountNo="2001405" currency="EUR" debit="25123.11" openingDate="2005-01-13 10:10:34"/>
</entry>
<entry>
<key>2000123</key>
<account accountNo="2000123" currency="EUR" debit="332.54" openingDate="2003-11-04 09:15:34"/>
</entry>
</data>
...
</formresult>
Circular object references are handled by adding a xpathref attribute to
the according element. Its value is an absolute XPath expression referencing the according
object's element:
<formresult serial="1199702214819">
...
<data>
<account accountNo="2000123">
<customer customerId="100000">
<accounts>
<account accountNo="2000000">
<customer xpathref="/formresult/data[1]/account[1]/customer[1]"/>
</account>
<account xpathref="/formresult/data[1]/account[1]"/>
<account accountNo="2001405">
<customer xpathref="/formresult/data[1]/account[1]/customer[1]"/>
</account>
</accounts>
</customer>
</account>
</data>
...
</formresult>
In this example the Account bean has a reference to a Customer
bean, which itself has a reference to all of its Accounts. You can see that all
beans, which were already serialized (as ancestors in the tree) contain an according back-reference.
Strings that contain XML code can be inserted as XML fragment the the resulting document by using
the @XMLFragmentSerializer:
public class FragmentBean {
private String myFragment = "<foo><bar baz=\"true\"/>character data</foo>";
@XMLFragmentSerializer
public String getMyFragment() {
return myFragment;
}
}
The XML, that is returned by the getMyFragment method is not treated
as a simple string, but as an XML fragment and thus, the content is not escaped, when
inserted in the document:
<?xml version="1.0" encoding="utf-8"?>
<result>
<myFragment>
<foo>
<bar baz="true"/>character data</foo>
</myFragment>
</result>
If you don't like the default serialization mechanism or you use unsupported types, you can
write your own serializers. There are two types of serializers: SimpleTypeSerializers,
which can produce String values (e.g. for primitive types),
and ComplexTypeSerializers, which can produce structured XML data
(e.g. for bean types).
Implementing your own serializer just requires to implement the SimpleTypeSerializer
or ComplexTypeSerializer interface and create a custom annotation to be able
to attach your serializer to a bean property.
Let's look at an example of a SimpleTypeSerializer:
a custom String serializer, which allows to configure if
Strings should be ouput lower- or uppercase. Here's the implementation:
...
import de.schlund.pfixcore.oxm.impl.AnnotationAware;
import de.schlund.pfixcore.oxm.impl.SimpleTypeSerializer;
import de.schlund.pfixcore.oxm.impl.annotation.StringSerializer;
...
public class StringTypeSerializer implements SimpleTypeSerializer, AnnotationAware {
private boolean doLowerCase;
public void setAnnotation(Annotation annotation) {
StringSerializer s=(StringSerializer)annotation;
doLowerCase=s.value();
}
public String serialize(Object obj, SerializationContext context) throws SerializationException {
if(obj instanceof String) {
String str=(String)obj;
if(doLowerCase) str=str.toLowerCase();
else str=str.toUpperCase();
return str;
}
throw new SerializationException("Illegal type: "+obj.getClass().getName());
}
}
The serializer implements the SimpleTypeSerializer interface.
Its serialize method checks if the passed object is of type String
and calls toLowerCase or toUpperCase before returning
the new String. The doLowerCase property controls which method is used.
This property is set within the setAnnotation method. The method is defined
in the AnnotationAware interface. This method is called by the framework
after the serializer is instantiated and passes the annotation set at the according bean
property. So you can access the configured values and configure your serializer.
Let's look at the according annotation definition:
...
@SimpleTypeSerializerClass(StringTypeSerializer.class)
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface StringSerializer {
boolean value();
}
You have to annotate the custom annotation with a SimpleTypeSerializerClass
annotation with the serializer class as value, make the annotation available to
methods and fields using the @Target annotation and make it visible
at runtime using the @Retention annotation. The rest of the annotation
definition can be done according to your needs. In the example we just define a boolean
property indicating if the String should be converted to lower- or uppercase. Here you
see how the annotation is applied to serialize a customer's lastname as uppercase:
public class Customer {
...
@StringSerializer(false)
public String getLastName() {...}
}
Let's look at an example of a ComplexTypeSerializer. We want to customize
the serialization of a Customer bean: the firstName and
lastName properties should be output together within a name
element:
public class Customer {
...
public long getCustomerId() {...}
public String getFirsstName() {...}
public String getLastName() {...}
public List<Account> getAccounts() {...}
...
}
The serializer just implements ComplexTypeSerializer. We don't need to
implement AnnotationAware because our annotation will have no parameter
we may want to read:
public class CustomerTypeSerializer implements ComplexTypeSerializer {
public void serialize(Object obj, SerializationContext context, XMLWriter writer) throws SerializationException {
if(obj instanceof Customer) {
Customer customer=(Customer)obj;
writer.writeStartElement("name");
writer.writeCharacters(customer.getFirstName()+" "+customer.getLastName());
writer.writeEndElement("name");
context.serialize(customer.getAccounts(),writer);
} else throw new SerializationException("Illegal type: "+obj.getClass().getName());
}
}
The serialize method gets a XMLWriter object, which
is used to write the name element. Then the passed SerializationContext
is used to serialize the customer's accounts using the default serialization mechanism.
Finally we implement a custom annotation:
@ComplexTypeSerializerClass(de.schlund.pfixcore.example.bank.oxm.CustomerTypeSerializer.class)
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomerSerializer {}
Here we apply the annotation to the Account bean's
customer property:
public class Account {
...
@CustomerSerializer
public Customer getCustomer() {...}
...
}
Here's an excerpt of the resulting XML:
<formresult serial="1199705488403">
...
<data>
<account accountNo="2000000" balance="3124.49" currency="EUR" openingDate="2003-10-23 08:05:22">
<customer>
<name>Mike Foo</name>
<account accountNo="..."/>
<account accountNo="..."/>
...
</customer>
</account>
</data>
...
</formresult>
The annotation-based IWrapper creation provides an alternative to the usual, XML configuration based, IWrapper creation. Using this approach you create IWrappers from standard Java Beans by adding the necessary configuration data in the form of annotations.
The IWrappers are automatically created during the build process using the Sun JVM's apt
tool and a custom AnnotationProcessor which analyzes the Java bean's
source code and generates the according IWrapper sources.
You're making a bean to a template for an IWrapper by adding an
@IWrapper annotation to its class declaration. By default every
bean property that is of a so-called builtin type, i.e. has a pre-defined
IWrapperParamCaster implementation, will be automatically added
as an IWrapper parameter.
Builtin types are boolean, byte, double, float, int, long,
java.lang.Boolean, java.lang.Byte, java.lang.Double, java.lang.Float, java.lang.Integer,
java.lang.Long, java.lang.String, java.util.Date and Arrays
with components of these types.
Bean properties of an unknown type are either ignored or require a @Caster
annotation specifying an appropriate caster. Bean properties can be annotated at their
getter methods or at the field itself, if it's public. If a property of a builtin type should
be skipped you can mark the according property with a @Transient annotation.
Bean based IWrappers can be used to create new beans or fill existing beans with
the IWrapper's state. Therefore the IWrapperToBean class provides
the two static methods <T> T createBean(IWrapper wrapper, Class<T> beanClass)
and populateBean(IWrapper wrapper, Object obj).
Every IWrapper configuration element known from the XML configuration has an annotation
counterpart. Besides there are some special annotations like @IWrapper
and @Transient. In the following we'll give a short overview of
all avaible annotations:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface IWrapper {
String name() default "";
Class<? extends IHandler> ihandler() default IHandler.class;
}
@IWrapper(name="MyBeanWrapper",ihandler=MyBeanHandler.class)
public class MyBean {
...
}
The @IWrapper annotation is used to mark a class as template for
an IWrapper. The name attribute denotes the class name of the
generated IWrapper class (without package). By default the bean name with the
suffix Wrapper is used (and the same package). The ihandler
attribute denotes the IHandler implementation class.
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Param {
String name() default "";
boolean mandatory() default true;
boolean trim() default true;
String missingscode() default "";
String[] defaults() default {};
}
@IWrapper(name="MyBeanWrapper",ihandler=MyBeanHandler.class)
public class MyBean {
...
@Param(name="MyValue",mandatory=false)
public int getValue() {...}
}
The @Param annotation is used to mark a bean property as parameter
and configure its name and all the other options known from the
IWrapper XML configuration. This annotation is optional, leaving it out, the property name
is used as name and the other attributes are set to their default values.
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Caster {
Class<? extends IWrapperParamCaster> type();
Property[] properties() default {};
}
@IWrapper(name="MyBeanWrapper",ihandler=MyBeanHandler.class)
public class MyBean {
...
@Caster(type=SomeClassCaster.class)
public SomeClass getValue() {...}
}
The @Caster annotation denotes the caster implementation class.
The nested properties attribute can be used to set properties via
@Property annotations. That's the same as the cparam
elements in the XML configuration (the params/properties are set using according methods
prefixed with put_).
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Property {
String name();
String value();
}
The @Property annotation is used as nested annotation within
the properties array attribute of various annotations. It consists
of simple name/value pairs.
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PreCheck {
Class<? extends IWrapperParamPreCheck> type();
Property[] properties() default {};
}
@IWrapper(name="MyBeanWrapper",ihandler=MyBeanHandler.class)
public class MyBean {
...
@PreCheck(
type=de.schlund.pfixcore.generator.prechecks.RegexpCheck.class,
properties={
@Property(name="regexp",value="/^(M|L|XL)$/")
}
)
public String getValue() {...}
}
The @PreCheck annotation denotes the precheck implementation class
with optional properties/parameters.
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PostCheck {
Class<? extends IWrapperParamPostCheck> type();
Property[] properties() default {};
}
@IWrapper(name="MyBeanWrapper",ihandler=MyBeanHandler.class)
public class MyBean {
...
@PostCheck(
type=de.schlund.pfixcore.generator.postchecks.IntegerRange.class,
properties={
@Property(name="range",value="0:2")
}
)
public int getValue() {...}
}
The @PostCheck annotation denotes the postcheck implementation class
with optional properties/parameters.
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Transient {}
@IWrapper(name="MyBeanWrapper",ihandler=MyBeanHandler.class)
public class MyBean {
...
@Transient
public int getValue() {...}
}
The @Transient annotation can be used to avoid that a bean property
of a builtin type is made to an IWrapper parameter.
Table of Contents
FireDebug is a JAVA implementation of FirePHP for Pustefix. It enables you to send log messages to Firefox and display them via FireBug. You can find more informations about FirePHP at http://www.firephp.org.
FireDebug is implemented as a ContextResource, so you can call it from everywhere
within Pustefix as long as you have access to the Context.
<context defaultflow="DefaultFlow"> <resource class="org.pustefixframework.util.firedebug.FireDebugImpl"> <implements class="org.pustefixframework.util.firedebug.FireDebug" /> </resource> </context>
To log messages just fetch the FireDebug ContextResource and call one of the different log methods.
FireDebug firePHP = (FireDebug) getContextResource(FireDebug.class); firePHP.log(a * b, "Multipliziere a(" + a + ") * b(" + b + ")");
To set the needed response headers you have to use either the FireDebugEnabledState
<pagerequest name="Calculator"> <state class="org.pustefixframework.util.firedebug.FireDebugEnabledState" /> </pagerequest>
or the FireDebugServletFilter or you can use both.
<servlet name="webservice"> <active>true</active> <class>de.schlund.pfixcore.webservice.WebServiceServlet</class> <propfile>ajax-calculator/conf/webservice.conf.xml</propfile> <use-filter>FirePHPServletFilter</use-filter> </servlet>