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 > Multitenancy Support
Date Mon, 05 Nov 2012 09:27:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/2042/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/Multitenancy+Support">Multitenancy
Support</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-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >{code} <br> <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3.
AdapterFactory <br> <br>The Tenant provider bundle should also register an {{AdapterFactory}}
to adapt {{ResourceResolver}} and other objects to {{Tenant}} instances. <br> <br>
<br></td></tr>
            <tr><td class="diff-unchanged" >h3. SlingHttpServletRequest <br>
<br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
    
            </table>
    </div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h1><a name="MultitenancySupport-MultitenancySupport"></a>Multitenancy
Support</h1>



<p>Status: DRAFT<br/>
Created: 2. April 2009<br/>
Author: fmeschbe<br/>
JIRA: -<br/>
References: -<br/>
Updated: -</p>

<div>
<ul>
    <li><a href='#MultitenancySupport-ProblemScope'>Problem Scope</a></li>
    <li><a href='#MultitenancySupport-ProposedSolution'>Proposed Solution</a></li>
    <li><a href='#MultitenancySupport-ProposedAPI'>Proposed API</a></li>
<ul>
    <li><a href='#MultitenancySupport-Tenant'>Tenant</a></li>
    <li><a href='#MultitenancySupport-TenantProvider'>TenantProvider</a></li>
    <li><a href='#MultitenancySupport-AdapterFactory'>AdapterFactory</a></li>
    <li><a href='#MultitenancySupport-SlingHttpServletRequest'>SlingHttpServletRequest</a></li>
    <li><a href='#MultitenancySupport-SlingHttpServletRequestWrapper'>SlingHttpServletRequestWrapper</a></li>
</ul>
</ul></div>


<h2><a name="MultitenancySupport-ProblemScope"></a>Problem Scope</h2>

<p>Consider a hosting service provider, who wants to use Sling as the server to run
the sites for different customers in the same Sling instance. Here you might want to separate
the resolution of resources for each client.</p>

<p>For example some client C1 will have its components below <tt>/apps/c1</tt>,
client C2 will have its components below <tt>/apps/c2</tt> and the service provider
has shared components below <tt>/apps/shared</tt>. To shield the client components
from each other, being able to have per-client search paths &#8211; as returned from <tt>ResourceResolver.getSearchPath()</tt>
&#8211; would be a nice feature. This way, requests to client C1's site would have a search
path of <tt>[ "/apps/c1", "/apps/shared" ]</tt> and requests to client C2's site
would have search path <tt>[ "/apps/c2", "/apps/shared" ]</tt>.</p>


<h2><a name="MultitenancySupport-ProposedSolution"></a>Proposed Solution</h2>

<p>The Sling API defines a new <tt>Tenant</tt> interface to represant tenants
and a <tt>TenantProvider</tt> service interface which provides access to tenants.
The tenant applicable to a given request is available through the new <tt>SlingHttpServletRequest.getTenant()</tt>
method. </p>

<p>Basically a tenant has just three defined properties which are mainly intended to
identify the tenant and list it for human consumption:</p>

<ul>
	<li>A globally unique identifier</li>
	<li>A (short) name, which is not required to be unique</li>
	<li>A (longer) description</li>
</ul>


<p>In addition a tenant may provide a number of application specific properties. The
properties are name value pairs, where the names are strings and the values may be of any
type.</p>

<p>One application of the tenant and its property will be the <tt>JcrResourceResolverFactory</tt>.
A new <tt>getResourceResolver(Session, Tenant)</tt> method is defined, where the
tenant is used to inject configuration for the <tt>ResourceResolver</tt> returned.
In a first step the <tt>resource.resolver.searchpath</tt> tenant property is used
to enhance the globally configured search path. If the property is set and can be converted
to a <tt>String[]</tt> the entries are merged with the global search path as follows:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-object">String</span>[] globalPath = <span class="code-comment">//
globally configured search path
</span><span class="code-object">String</span>[] tenantPath = <span class="code-comment">//
per-tenant search path
</span>List&lt;<span class="code-object">String</span>&gt; searchPath
= <span class="code-comment">// search path merged from globalPath and tenantPath
</span>
<span class="code-comment">// add absolute tenantPath entries
</span><span class="code-keyword">for</span> each entry in tenantPath
   <span class="code-keyword">if</span> entry is absolute then searchPath.add(entry);
done

<span class="code-comment">// add relative tenantPath entries
</span><span class="code-keyword">for</span> each globalEntry in globalPath
   <span class="code-keyword">for</span> each entry in tenantPath
      <span class="code-keyword">if</span> entry is relative searchPath.add(globalEntry
+ <span class="code-quote">"/"</span> + entry);
   done
   searchPath.add(globalEntry);
done
</pre>
</div></div>

<p>Example: For a globalPath of <tt>[ "/apps", "/libs" ]</tt> and a tenantPath
of <tt>[ "/tenant", "c1", "shared" ]</tt> the merged search path will be <tt>[
"/tenant", "/apps/c1", "/apps/shared", "/apps", "/libs/cq", "/libs/shared", "/libs"]</tt>.</p>

<p>If the <tt>resource.resolver.searchpath</tt> property of the tenant does
not exist or if no tenant is available to the <tt>JcrResourceResolverFactory</tt>
the global search path configuration is used unmodified.</p>

<p>In the future more properties might be used to augment the configuration of a resource
resolver returned for a given tenant.</p>


<h2><a name="MultitenancySupport-ProposedAPI"></a>Proposed API</h2>


<h3><a name="MultitenancySupport-Tenant"></a>Tenant</h3>

<p>The <tt>Tenant</tt> interface is defined in the <tt>org.apache.sling.api.tenant.Tenant</tt>
package of the Sling API bundle <tt>org.apache.sling.api</tt>.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>Tenant.java</b></div><div class="codeContent
panelContent">
<pre class="code-java">
/**
 * The &lt;code&gt;Tenant&lt;/code&gt; <span class="code-keyword">interface</span>
represents a tenant which may be used to
 * further customize request and other processing.
 * &lt;p&gt;
 * This <span class="code-keyword">interface</span> is intended to be implemented
by the implementor of the
 * {@link TenantProvider} <span class="code-keyword">interface</span> to be returned
<span class="code-keyword">for</span> it tenant accessor
 * methods.
 */
<span class="code-keyword">public</span> <span class="code-keyword">interface</span>
Tenant {

    /**
     * The name of the {@link #getProperty(<span class="code-object">String</span>)
property} whose string
     * representation is used as <span class="code-keyword">this</span> tenant's
{@link #getName() name} (value is
     * <span class="code-quote">"sling.tenant.name"</span>).
     * 
     * @see #getName()
     * @see #getProperty(<span class="code-object">String</span>)
     */
    <span class="code-keyword">static</span> <span class="code-keyword">final</span>
<span class="code-object">String</span> PROP_NAME = <span class="code-quote">"sling.tenant.name"</span>;

    /**
     * The name of the {@link #getProperty(<span class="code-object">String</span>)
property} whose string
     * representation is used as <span class="code-keyword">this</span> tenant's
{@link #getDescription()
     * description} (value is <span class="code-quote">"sling.tenant.description"</span>).
     * 
     * @see #getDescription()
     * @see #getProperty(<span class="code-object">String</span>)
     */
    <span class="code-keyword">static</span> <span class="code-keyword">final</span>
<span class="code-object">String</span> PROP_DESCRIPTION = <span class="code-quote">"sling.tenant.description"</span>;

    /**
     * Returns the unique identifier of <span class="code-keyword">this</span>
tenant.
     * &lt;p&gt;
     * The tenant identifier has not predefined mapping to a property and may be
     * generated automatically by the TenantProvider.
     */
    <span class="code-object">String</span> getId();

    /**
     * Returns the name of the tenant. This is a <span class="code-object">short</span>
name <span class="code-keyword">for</span> quickly
     * identifying <span class="code-keyword">this</span> tenant. This name is
not required to be globally unique.
     * &lt;p&gt;
     * The name of the tenant is the string representation of the
     * {@link #PROP_NAME} property.
     */
    <span class="code-object">String</span> getName();

    /**
     * Returns a human readable description of <span class="code-keyword">this</span>
tenant.
     * &lt;p&gt;
     * The description of the tenant is the string representation of the
     * {@link #PROP_DESCRIPTION} property.
     */
    <span class="code-object">String</span> getDescription();

    /**
     * Returns the named property or &lt;code&gt;<span class="code-keyword">null</span>&lt;/code&gt;
<span class="code-keyword">if</span> no such property
     * exists or <span class="code-keyword">if</span> the property value itself
is &lt;code&gt;<span class="code-keyword">null</span>&lt;/code&gt;.
     */
    <span class="code-object">Object</span> getProperty(<span class="code-object">String</span>
name);

    /**
     * Returns the named property converted to the requested type or
     * &lt;code&gt;<span class="code-keyword">null</span>&lt;/code&gt;
<span class="code-keyword">if</span> the property does not exist, the property
value
     * itself is &lt;code&gt;<span class="code-keyword">null</span>&lt;/code&gt;
or cannot be converted to the requested type.
     */
    &lt;Type&gt; Type getProperty(<span class="code-object">String</span>
name, Type type);

    /**
     * Returns an iterator or <span class="code-object">String</span> values representing
the names of defined
     * properties of <span class="code-keyword">this</span> tenant.
     */
    Iterator&lt;<span class="code-object">String</span>&gt; getPropertyNames();
}
</pre>
</div></div>


<h3><a name="MultitenancySupport-TenantProvider"></a>TenantProvider</h3>

<p>The <tt>TenantProvider</tt> interface is defined in the <tt>org.apache.sling.api.tenant.Tenant</tt>
package of the Sling API bundle <tt>org.apache.sling.api</tt>.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>TenantProvider.java</b></div><div
class="codeContent panelContent">
<pre class="code-java">
/**
 * The &lt;code&gt;TenantProvider&lt;/code&gt; defines the service <span
class="code-keyword">interface</span> of <span class="code-keyword">for</span>
a sevice
 * which may be asked <span class="code-keyword">for</span> {@link Tenant tenant
instances}.
 * &lt;p&gt;
 * For now <span class="code-keyword">this</span> provider <span class="code-keyword">interface</span>
provides access to a tenant applying to a
 * particular request as well as to all tenants known to <span class="code-keyword">this</span>
provider.
 */
<span class="code-keyword">public</span> <span class="code-keyword">interface</span>
TenantProvider {

    /**
     * Returns the {@link Tenant} with the given &lt;code&gt;tenantId&lt;/code&gt;
or
     * &lt;code&gt;<span class="code-keyword">null</span>&lt;/code&gt;
<span class="code-keyword">if</span> no such tenant exists.
     */
    Tenant getTenantById(<span class="code-object">String</span> tenantId);

    /**
     * Returns an iterator of all {@link Tenant tenants} known to <span class="code-keyword">this</span>
provider.
     * If no tenants are known the iterator is empty.
     */
    Iterator&lt;Tenant&gt; getTenants();

    /**
     * Returns an iterator of {@link Tenant tenants} whose names match the given
     * &lt;code&gt;tenantName&lt;/code&gt;. Since the name of a tenant is
not required to be
     * globally unique, <span class="code-keyword">this</span> method may <span
class="code-keyword">return</span> more than one tenant.
     * &lt;p&gt;
     * If no tenants are known with the requested name the iterator is empty.
     */
    Iterator&lt;Tenant&gt; getTenantsByName(<span class="code-object">String</span>
tenantName);

    /**
     * Returns an iterator of {@link Tenant tenants} matching the given
     * &lt;code&gt;tenantFilter&lt;/code&gt;.
     * &lt;p&gt;
     * The &lt;code&gt;tenantFilter&lt;/code&gt; is a valid OSGi filter string
as defined in
     * Section 3.2.6, Filter Syntax, of the OSGi Core Specification, Release 4.
     * &lt;p&gt;
     * If no tenants match the &lt;code&gt;tenantFilter&lt;/code&gt; or the
     * &lt;code&gt;tenantFilter&lt;/code&gt; is not a valid filter string
the iterator is
     * empty.
     */
    Iterator&lt;Tenant&gt; getTenantsByFilter(<span class="code-object">String</span>
tenantFilter);
}
</pre>
</div></div>

<h3><a name="MultitenancySupport-AdapterFactory"></a>AdapterFactory</h3>

<p>The Tenant provider bundle should also register an <tt>AdapterFactory</tt>
to adapt <tt>ResourceResolver</tt> and other objects to <tt>Tenant</tt>
instances.</p>


<h3><a name="MultitenancySupport-SlingHttpServletRequest"></a>SlingHttpServletRequest</h3>

<p>The <tt>SlingHttpServletRequest</tt> interface is extended to return
the <tt>Tenant</tt> applicable for the request.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>SlingHttpServletRequest.java</b></div><div
class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> <span class="code-keyword">interface</span>
SlingHttpServletRequest <span class="code-keyword">extends</span> HttpServletRequest
{
    ...
    /**
     * Returns the {@link Tenant} applicable to <span class="code-keyword">this</span>
request or
     * &lt;code&gt;<span class="code-keyword">null</span>&lt;/code&gt;
<span class="code-keyword">if</span> no tenant can be resolved <span class="code-keyword">for</span>
<span class="code-keyword">this</span> request.
     */
    Tenant getTenant();
    ...
}
</pre>
</div></div>


<h3><a name="MultitenancySupport-SlingHttpServletRequestWrapper"></a>SlingHttpServletRequestWrapper</h3>

<p>The <tt>SlingHttpServletRequestWrapper</tt> interface is extended to
return the <tt>Tenant</tt> applicable for the request.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>SlingHttpServletRequestWrapper.java</b></div><div
class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> class SlingHttpServletRequestWrapper
<span class="code-keyword">extends</span> HttpServletRequestWrapper
        <span class="code-keyword">implements</span> SlingHttpServletRequest {
    ...
    <span class="code-keyword">public</span> Tenant getTenant() {
        <span class="code-keyword">return</span> getSlingRequest().getTenant();
    }
    ...
}
</pre>
</div></div>


<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>JcrResourceResolverFactory.java</b></div><div
class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> <span class="code-keyword">interface</span>
JcrResourceResolverFactory {
    ...
    ResourceResolver getResourceResolver(Session session, Tenant tenant);
    ...
}
</pre>
</div></div>
    </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/Multitenancy+Support">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=113873&revisedVersion=5&originalVersion=4">View
Changes</a>
                |
        <a href="https://cwiki.apache.org/confluence/display/SLING/Multitenancy+Support?showComments=true&amp;showCommentArea=true#addcomment">Add
Comment</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message