Page Status
This is obsolete content copied from the Sling website on 2010-02-04.
Default Content Mapping and Request Rendering
Page Status
2008-02-13: this page is out of sync with the current codebase, needs to be reviewed and updated.
One of the big obstacles in quick adoption of Sling might arguably be the requirement for multiple developments, such as...
- Creating a
Content
implementation (or decide on reusing an existing implementation) - Defining the mapping descriptor to map the repository contents to the
Content
object and vice versa - Optionally create a node type defintion file in CND format
- Creating a
Component
implementation (or decide on reusing an existing implementation) - Package this all up into an OSGi Bundle for deployment
While these steps make sense in an ideal world we all know does not exist (with the exception of Utopia, but there are no computers in Utopia), helpers for rapid development are needed. These helpers come in the form of usefull defaults on various levels.
Default Content Mapping
When a request is processed by Sling, one step is to resolve the request URL into a Content
object. This works by checking the request URL for the longest match with an existing JCR repository node. The path of this node is then used to load the Content
object through the ContentManager.load(String)
method. If no mapping exists for the given node, an exception is thrown and the request fails.
In such a case of missing content mapping, a default Content
mapping is defined in the form of the org.apache.sling.content.jcr.DefaultContent
class. This mapping has the following features:
- The
DefaultContent
class is ajava.util.Map
. Thus all properties may be accessed using the familiarMap
API. - All non-protected properties of the node are loaded. Single value properties become scalar objects, while multi value properties become
java.util.List
objects. - The types of the repository values are mapped according to the JCR specification for mapping between Property types and Java types.
- A few properties have special significance. See below.
- Creating new instances of this class and inserting these into the repository creates nodes of type
nt:unstructured
. When loading instances of this class the actual primary type of the node does not matter.
Property |
Type |
Description |
---|---|---|
|
String |
The path of the node from which the content was loaded. This must not be modified by application programs, unless you are prepared for unexpected behaviour when storing the object. |
|
String |
The primary node type of the (existing) node. This property is purely informational and will never be used when inserting new content or writing back content. |
|
List of String |
The mixin node types of the (existing) node. This property is purely informational and will never be used when inserting new content or writing back content. If the node has no mixin node types, this property does not exist. |
|
String |
The component ID of the component used to handle requests to this content. This property may be modified by application programs (though you should be aware of the consequences) and is used as the result of the |
Default Component Selection
After having mapped the JCR repository node into the Content
object the Component
to actually handle the request must be resolved. This is done by calling the Content.getComponentId()
method and looking up this component ID in an internal table. If either the Content.getComponentId()
method returns null
or no component is registered with the requested component ID a default resolution processing takes place as follows:
- Let
cid
be the result of callingContent.getComponentId()
- If
cid
isnull
, letcid
be the result of callingContent.getPath()
(this is nevernull
} - Check for a component with the given
cid
and use it if existing - Otherwise, remove any leading slash from
cid
and replace slashes by dots and check for a component with this modifiedcid
and use it if existing - Otherwise, let
cid
be the fully qualified name of theContent
object class and check for a component with this modifiedcid
and use it if existing - Otherwise and if
cid
ends with the string Content, remove that suffix and check for a component with this modifiedcid
and use it if existing - Otherwise, append
Component
to the end ofcid
and and check for a component with this modifiedcid
and use it if existing - Otherwise, let
cid
be the value of theorg.apache.sling.components.DefaultComponent.ID
field and check for a component with this modifiedcid
and use it if existing - Finally, fail without having found a component to use - this is highly unlikely, though, because the default component is part of the Sling Core bundle and should always be available.
DefaultComponent
The default component first checks whether the request is sent with parameters and will update the Content
object with the parameters as follows:
- If the
Content
object is ajava.util.Map
(such as is the case for theDefaultContent
) the properties will directly accessed through theMap
} API. Otherwise, theContent
object is wrapped inside aorg.apache.commons.collections.BeanMap
to access the fields through theMap
API. - Any properties listed in the
_delete
parameter are removed. The_delete
parameter may contain a comma-separated list of property names and may occurr multiple times. - All other parameters are used to set property values, where any existing properties will be replaced and all properties not listed in the parameters remain unmodified. If a parameter occurrs only once a single value property is set, if parameter occurrs multiple times, a multi value property is set as a list of strings. Note, that any data type conversion may happen only by the
BeanMap
as required and thus lead to failure to update a single proeperty.
After the optional update phase, the fields of the Content
object are written back. Again, the Content
object is either accessed as a Map
directly if it is a Map
or packed in a BeanMap
otherwise. The format of the output is deduced from the request URL's extension as returned by the ComponentRequest.getExtension()
method:
Extension |
Format |
---|---|
|
HTML, UTF-8 encoded |
|
XML, UTF-8 encoded |
|
Plain text, UTF-8 encoded |
|
Java Properties file format suitable for a normal properties file |
|
JSON, UTF-8 encoded |
Default Script
The easiest way to develop and deploy a component is to create a scripted component in the repository by just creating a node of type sling:scriptedComponent
and creating a single JSP script at jsp/start.jsp
below the component node. After that you can refer to that component by the path of the component node and get the start.jsp
script called.
For more more elaborate script selection you may of course create more scripts and refer to them below the sling:scripts
node of the component node.
Rapid Development Primer
To summarize, for rapid development you will have to execute the following steps:
- Create a
sling:ScriptedComponent
node, for example at/some/sample/component
- Create a JSP script file at
jsp/start.jsp
below that node; that would be/some/sample/component/jsp/start.jsp
in the example - Create one or more nodes of any type, for example
nt:unstructured
, which have a single value string property namedsling:componentId
referring to the component via its path - Request the node by typing its path in your browser's address field