incubator-sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Sling > Sling API Redesign
Date Mon, 07 Feb 2011 10:19:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/2036/9/1/_/styles/combined.css?spaceKey=SLING&amp;forWysiwyg=true"
type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://cwiki.apache.org/confluence/display/SLING/Sling+API+Redesign">Sling
API Redesign</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~fmeschbe">Felix
Meschberger</a>
    </h4>
        <br/>
                         <h4>Changes (1)</h4>
                                 
    
<div id="page-diffs">
                    <table class="diff" cellpadding="0" cellspacing="0">
    
            <tr><td class="diff-added-lines" style="background-color: #dfd;">{excertop:hide=true}(IMPLEMENTED){excerpt}
<br> <br></td></tr>
            <tr><td class="diff-unchanged" >h1. Redesign of the Sling API <br>
<br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
    
            </table>
    </div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <div class="error"><span class="error">Unknown macro: {excertop}</span>
</div>
<p>(IMPLEMENTED)</p>

<h1><a name="SlingAPIRedesign-RedesignoftheSlingAPI"></a>Redesign of the
Sling API</h1>

<p>Status: IMPLEMENTED</p>

<p>There have been a number of threads on the Sling Dev Mailing List on simplifying
the current <em>Component API</em> and turn it into a new Sling API. This page
starts at the current state of the discussion as of Oct. 10, 2007, and tries to summarize
what has been discussed and to resolve this into a modified proposal.</p>

<div>[ <a href='#SlingAPIRedesign-RedesignoftheSlingAPI'>Redesign of the Sling
API</a> ] [ <a href='#SlingAPIRedesign-References'>References</a> ] [ <a
href='#SlingAPIRedesign-CurrentState'>Current State</a> ] [ <a href='#SlingAPIRedesign-UpdateModified%7B%7BContent%7D%7D'>Update
Modified <tt>Content</tt></a> ] [ <a href='#SlingAPIRedesign-JCRbasedOperations'>JCR
based Operations</a> ] [ <a href='#SlingAPIRedesign-Replace%7B%7BContent%7D%7Dby%7B%7BResource%7D%7D'>Replace
<tt>Content</tt> by <tt>Resource</tt></a> ] [ <a href='#SlingAPIRedesign-Extensionstothe%7B%7BResource%7D%7Dinterface'>Extensions
to the <tt>Resource</tt> interface</a> ] [ <a href='#SlingAPIRedesign-OpenIssues'>Open
Issues</a> ] [ <a href='#SlingAPIRedesign-ResolvingtheServlet'>Resolving the Servlet</a>
]</div>


<h2><a name="SlingAPIRedesign-References"></a>References</h2>

<ul>
	<li><a href="http://issues.apache.org/jira/browse/SLING-28" class="external-link"
rel="nofollow">SLING-28, Simplify the Sling (aka Component) API</a></li>
	<li><a href="http://issues.apache.org/jira/browse/SLING-47" class="external-link"
rel="nofollow">SLING-47, microsling</a>, "Sling reduced to the max"</li>
	<li><a href="http://www.mail-archive.com/sling-dev@incubator.apache.org/msg00177.html"
class="external-link" rel="nofollow">Simplifying our component api</a> - The original
thread launched by Carsten</li>
	<li><a href="http://www.mail-archive.com/sling-dev@incubator.apache.org/msg00267.html"
class="external-link" rel="nofollow">Move ContentManager to Sling API</a> - My own
proposal to make the ContentManager part of the Sling API</li>
	<li><a href="http://www.mail-archive.com/sling-dev@incubator.apache.org/msg00288.html"
class="external-link" rel="nofollow">Breaking Sling into smaller pieces?</a> - Bertrand's
proposal to further modularize parts of Sling such as the current <em>sling-core</em>
bundle</li>
</ul>




<h2><a name="SlingAPIRedesign-CurrentState"></a>Current State</h2>

<p>Currently, request processing is controlled by the <em>sling-core</em>
bundle using two sets of filters: one set called at the time the client request enters Sling
- so called request level filters - and the other set called for each Content object processed
during request processing - so called content level filters.</p>

<p>Amongst the request level filters is the <tt>ContentResolverFilter</tt>
which takes the request URL and finds a <tt>Content</tt> object for the URL. This
filter implements the <tt>ContentResolver</tt> interface and is also registered
as this service. So other parts of the system may use the same mechanism to resolve paths
to <tt>Content</tt> objects. The <tt>ContentResolver</tt> also implements
the default content loading described <a href="/confluence/display/SLING/Default+Mapping+and+Rendering+%28OBSOLETE%29"
title="Default Mapping and Rendering (OBSOLETE)">here</a>.</p>

<p>Amongst the content level filters is the <tt>ComponentResolverFilter</tt>
which asks the <tt>Content</tt> object for its component ID and resolves this
ID using the registered {{Component}}s. This filter also implements the default component
resolution described <a href="/confluence/display/SLING/Default+Mapping+and+Rendering+%28OBSOLETE%29"
title="Default Mapping and Rendering (OBSOLETE)">here</a>.</p>

<p>To manage content Sling provides two interfaces:</p>

<ul>
	<li><b><tt>ContentManager</tt></b> - Basic interface allowing
CRUD operations using <tt>Content</tt> objects. This interface is completely agnostic
of the actual persistence used.</li>
	<li><b><tt>JcrContentManager</tt></b> - Extends the <tt>ContentManager</tt>
interface integrating with the Jackrabbit OCM <tt>ObjectContentManager</tt> interface.
This provides the API actually used by the <tt>ContentResolverFilter</tt> to load
<tt>Content</tt> objects from the JCR repository according to the request URL.</li>
</ul>



<p>If components would want to create, update and delete content, they would access
the <tt>ContentManager</tt> by retrieving the <tt>org.apache.sling.jcr.content_manager</tt>
request attribute. If JCR tasks would have to be executed, that retrieved object would be
cast to <tt>JcrContentManager</tt> and the session retrieved.</p>

<p>Examples:</p>

<h5><a name="SlingAPIRedesign-UpdateModified%7B%7BContent%7D%7D"></a>Update
Modified <tt>Content</tt></h5>

<p>After having modified the content, a component might do the following to persisted
the modified content:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
Content content = componentRequest.getContent();

<span class="code-comment">// modify content
</span>
ContentManager contentManager = (ContentManager) componentRequest.getAttribute(<span class="code-quote">"org.apache.sling.jcr.content_manager"</span>);
contentManager.store(content);
contentManager.save();
</pre>
</div></div>


<h5><a name="SlingAPIRedesign-JCRbasedOperations"></a>JCR based Operations</h5>

<p>To operate on a JCR level or to directly access the JCR <tt>Node</tt>
underlying the request <tt>Content</tt> the following might be done:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

<span class="code-comment">// get the JCR content manager
</span>JcrContentManager jcrContentManager = (JcrContentManager) componentRequest.getAttribute(<span
class="code-quote">"org.apache.sling.jcr.content_manager"</span>);

<span class="code-comment">// get the session
</span>Session session = jcrContentManager.getSession();

<span class="code-comment">// access the node addressed by the request URL
</span><span class="code-object">String</span> contentPath = componentRequest.getContent().getPath();
Node contentNode = (Node) session.getItem(contentPath);
</pre>
</div></div>


<p>Arguably, this is tedious. So a first simplification proposal suggested to move the
JCR agnostic <tt>ContentManager</tt> interface to the Sling API and to provide
a getter method on the <tt>ComponentRequest</tt> interface. The returned object
might also be cast to a <tt>JcrContentManager</tt> to then access the repository.</p>

<p>This proposal sparked a series of reactions (see references above) and so based on
Bertrands thoughts, we propose the following change.</p>



<h2><a name="SlingAPIRedesign-Replace%7B%7BContent%7D%7Dby%7B%7BResource%7D%7D"></a>Replace
<tt>Content</tt> by <tt>Resource</tt></h2>

<p>The "problem" of the current Component API is that is centered around a <tt>Content</tt>
interface which presumably is data provided to the component loaded from the persistence (the
JCR repository of course) actually hiding the repository. This also predefines how data is
acquired and used, namely by using Object Content Mapping.</p>

<p>Starting off this situation, we propose replacing the (fully loaded) <tt>Content</tt>
by a data representation we will call <tt>Resource</tt>:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> <span class="code-keyword">interface</span>
Resource {

  <span class="code-comment">// the original request URL leading to the resource
</span>  <span class="code-comment">// <span class="code-keyword">this</span>
is not necessairily the same as ServletRequest.getRequestURL as
</span>  <span class="code-comment">// it may have been processed by some URL
mapping and folding
</span>  <span class="code-object">String</span> getOriginalURI();

  <span class="code-comment">// the path to the actual resource providing the data
</span>  <span class="code-comment">// from the point of view of Sling <span
class="code-keyword">this</span> is just a string
</span>  <span class="code-object">String</span> getURI();

  <span class="code-comment">// the selectors of the request or empty array <span
class="code-keyword">if</span> none
</span>  <span class="code-comment">// the selectors are dot-separated strings
after the part of
</span>  <span class="code-comment">// original URI addressing the resource upto
the extension
</span>  <span class="code-comment">// Examples:
</span>  <span class="code-comment">//    - /a/b/c has no selectors <span class="code-keyword">for</span>
resource /a/b/c
</span>  <span class="code-comment">//    - /a/b/c.html has no selectors <span
class="code-keyword">for</span> resource /a/b/c
</span>  <span class="code-comment">//    - /a/b/c.s1.s2.html has selectors [
s1, s2 ] <span class="code-keyword">for</span> resource /a/b/c
</span>  <span class="code-comment">//    - /a/b/c.s.html/suffix has selector
[ s ] <span class="code-keyword">for</span> resource /a/b/c
</span>  <span class="code-object">String</span>[] getSelectors();

  <span class="code-comment">// the extension of the request or empty string <span
class="code-keyword">if</span> none
</span>  <span class="code-comment">// the extension is a string after the last
dot after the
</span>  <span class="code-comment">// part of the original URI addressing the
resource upto the
</span>  <span class="code-comment">// end of the original URI or a slash
</span>  <span class="code-comment">// Examples:
</span>  <span class="code-comment">//    - /a/b/c has no extension <span class="code-keyword">for</span>
resource /a/b/c
</span>  <span class="code-comment">//    - /a/b/c.html has extension html <span
class="code-keyword">for</span> resource /a/b/c
</span>  <span class="code-comment">//    - /a/b/c.s1.s2.html has extension html
<span class="code-keyword">for</span> resource /a/b/c
</span>  <span class="code-comment">//    - /a/b/c.s.html/suffix has extension
html <span class="code-keyword">for</span> resource /a/b/c
</span>  <span class="code-object">String</span> getExtension();

  <span class="code-comment">// the suffix of the request or empty string <span class="code-keyword">if</span>
none
</span>  <span class="code-comment">// the suffix is the string after the next
slash after the part
</span>  <span class="code-comment">// of the original URI addressing the resource
</span>  <span class="code-comment">// Examples:
</span>  <span class="code-comment">//    - /a/b/c has no suffix <span class="code-keyword">for</span>
resource /a/b/c
</span>  <span class="code-comment">//    - /a/b/c.html has no suffix <span
class="code-keyword">for</span> resource /a/b/c
</span>  <span class="code-comment">//    - /a/b/c.s1.s2.html has no suffix <span
class="code-keyword">for</span> resource /a/b/c
</span>  <span class="code-comment">//    - /a/b/c.s.html/suffix has suffix suffix
<span class="code-keyword">for</span> resource /a/b/c
</span>  <span class="code-object">String</span> getSuffix();

}
</pre>
</div></div>

<p>The <tt>ComponentRequest</tt> interface would be modified as follows:</p>

<ul>
	<li>The <tt>getExtension()</tt>, <tt>getSelector(int)</tt>,
<tt>getSelectors()</tt>, <tt>getSelectorString()</tt> and <tt>getSuffix()</tt>
methods are removed as this information can now be obtained from the <tt>Resource</tt>
directly.</li>
	<li>The <tt>getContent()</tt>, <tt>getContent(String)</tt>,
<tt>getChildren(Content)</tt> and <tt>getRequestDispatcher(Content)</tt>
methods are replaced as follows:</li>
</ul>


<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> <span class="code-keyword">interface</span>
ComponentRequest <span class="code-keyword">extends</span> HttpServletRequest
{

  ...

  <span class="code-comment">// Returns the Resource to which the getRequestURL method
maps
</span>  Resource getResource();

  <span class="code-comment">// Returns a Resource to which the given URI <span class="code-object">String</span>
maps
</span>  <span class="code-comment">// Implicit: getResource().equals(getResource(getRequestURL()))
</span>  Resource getResource(<span class="code-object">String</span> uri);

  <span class="code-comment">// Returns an Enumeration child Resources of the given
Resource
</span>  <span class="code-comment">// If resource parameter is <span class="code-keyword">null</span>,
getResource() is used as parent
</span>  <span class="code-comment">// (use Enumeration to stay in line with the
HttpServletRequest)
</span>  Enumeration&lt;Resource&gt; getChildren(Resource resource);

  <span class="code-comment">// Gets a RequestDispatcher to include the given resource
</span>  RequestDispatcher getRequestDispatcher(Resource resource);

  ...

}
</pre>
</div></div>


<h3><a name="SlingAPIRedesign-Extensionstothe%7B%7BResource%7D%7Dinterface"></a>Extensions
to the <tt>Resource</tt> interface</h3>

<p>The <tt>Resource</tt> interface may be extended depending on the way,
the resource is acquired. For example, there might be a <tt>MappedContentResource</tt>
which would return an object mapped from any persistence layer, a <tt>JcrResource</tt>
may encapsulate a JCR based resource. A resolver loading content from a JCR repository using
Jackrabbit OCM might return a resource which implements both the <tt>MappedContentResource</tt>
and the <tt>JcrResource</tt> interfaces.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>MappedContentResource</b></div><div
class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> <span class="code-keyword">interface</span>
MappedContentResource <span class="code-keyword">extends</span> Resource {

  <span class="code-comment">// Returns the mapped data object
</span>  <span class="code-object">Object</span> getObject();

}
</pre>
</div></div>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>JcrResource</b></div><div class="codeContent
panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> <span class="code-keyword">interface</span>
JcrResource <span class="code-keyword">extends</span> Resource {

  <span class="code-comment">// Returns the JCR session used to acquire the Node
</span>  <span class="code-comment">// (<span class="code-keyword">this</span>
is actually convenience as getNode().getSession()
</span>  <span class="code-comment">// must <span class="code-keyword">return</span>
the same session)
</span>  Session getSession();

  <span class="code-comment">// Returns the JCR Node addressed by the Resource URI
</span>  <span class="code-comment">// <span class="code-keyword">this</span>
is the same as getSession().getItem(getURI());
</span>  Node getNode();

}
</pre>
</div></div>


<p>The existing ContentResolver will be retargeted to the <tt>Resource</tt>
interface and return an object implementing the <tt>MappedContentResource</tt>
and the <tt>JcrResource</tt> interfaces if a mapping exists. Otherwise an object
just implementing the <tt>JcrResource</tt> interface is returned providing just
the resolved node.</p>


<h2><a name="SlingAPIRedesign-OpenIssues"></a>Open Issues</h2>

<p>This above definition leaves a series of issues open.</p>


<h3><a name="SlingAPIRedesign-ResolvingtheServlet"></a>Resolving the Servlet</h3>

<div class='panelMacro'><table class='infoMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/information.gif" width="16"
height="16" align="absmiddle" alt="" border="0"></td><td>The <tt>Component</tt>
interface is removed and the <tt>Servlet</tt> interface is used.</td></tr></table></div>

<p>Currently the <tt>Content</tt> interface defines a method <tt>getComponentId()</tt>
which returns the identifier of a <tt>Component</tt> to which processing of the
request is dispatched. With the new <tt>Resource</tt> interface, no such method
exists any more.</p>

<p>The intent is, that <tt>Servlet</tt> resolver would know about the concrete
implementations of the <tt>Resource</tt> interface and could handle the respective
resources. For example the Sling standard servlet resolver could try the following:</p>

<ol>
	<li>If the <tt>Resource</tt> is a <tt>JcrResource</tt> check
the <tt>sling:servletId</tt> property of the resource node. If such a property
exists and denotes a registered <tt>Servlet</tt> service, that servlet is used.</li>
	<li>Otherwise, if the <tt>Resource</tt> is a <tt>MappedContentResource</tt>,
find a <tt>Servlet</tt> service willing to handle requests for the actual object
class of the mapped object. The <tt>Servlet</tt> service could be registered with
a service property listing the names of the mapped object classes supported.</li>
	<li>Otherwise try to find a registered <tt>Servlet</tt> interface willing
to handle the request using the resource path, selectors and/or extensions.</li>
</ol>


<p>Alternatively, the <tt>Resource</tt> interface might have a <tt>getServletId()</tt>
method providing the identifier of the servlet to use. It might well be that the first solution
is the better one.</p>
    </div>
        <div id="commentsSection" class="wiki-content pageSection">
        <div style="float: right;">
            <a href="https://cwiki.apache.org/confluence/users/viewnotifications.action"
class="grey">Change Notification Preferences</a>
        </div>
        <a href="https://cwiki.apache.org/confluence/display/SLING/Sling+API+Redesign">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=68399&revisedVersion=9&originalVersion=8">View
Changes</a>
                |
        <a href="https://cwiki.apache.org/confluence/display/SLING/Sling+API+Redesign?showComments=true&amp;showCommentArea=true#addcomment">Add
Comment</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message