You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 10:16:35 UTC

[sling-org-apache-sling-tenant] 01/18: SLING-2656 Commit a first shot at Multi Tenancy Support

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.tenant-1.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-tenant.git

commit dd950518da5ed0f367056e2619f1e5a642802fbb
Author: Felix Meschberger <fm...@apache.org>
AuthorDate: Fri Nov 9 22:26:03 2012 +0000

    SLING-2656 Commit a first shot at Multi Tenancy Support
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/tenant@1407672 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            | 142 ++++++++++++
 src/main/java/org/apache/sling/tenant/Tenant.java  |  96 ++++++++
 .../org/apache/sling/tenant/TenantProvider.java    |  60 +++++
 .../tenant/internal/TenantAdapterFactory.java      | 138 +++++++++++
 .../apache/sling/tenant/internal/TenantImpl.java   |  84 +++++++
 .../sling/tenant/internal/TenantProviderImpl.java  | 258 +++++++++++++++++++++
 .../java/org/apache/sling/tenant/package-info.java |  26 +++
 7 files changed, 804 insertions(+)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..8d68c85
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>13</version>
+        <relativePath>../../../parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.sling.tenant</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <name>Apache Sling Tenant</name>
+    <description>
+        This bundle provides the Tenant Provider Implementation and API.
+    </description>
+
+    <scm>
+        <connection>
+            scm:svn:http://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/tenant
+        </connection>
+        <developerConnection>
+            scm:svn:https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/tenant
+        </developerConnection>
+        <url>
+            http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/tenant
+        </url>
+    </scm>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.sling</groupId>
+                <artifactId>maven-sling-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate-adapter-metadata</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>generate-adapter-metadata</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+            </plugin>
+        </plugins>
+    </build>
+    
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.osgi</artifactId>
+            <version>2.1.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>jackrabbit-api</artifactId>
+            <version>2.4.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <version>4.3.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>biz.aQute</groupId>
+            <artifactId>bndlib</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Testing -->
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.testing</artifactId>
+            <version>2.0.6</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.8.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit-addons</groupId>
+            <artifactId>junit-addons</artifactId>
+            <version>1.4</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/tenant/Tenant.java b/src/main/java/org/apache/sling/tenant/Tenant.java
new file mode 100644
index 0000000..1f32733
--- /dev/null
+++ b/src/main/java/org/apache/sling/tenant/Tenant.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.tenant;
+
+import java.util.Iterator;
+
+/**
+ * The <code>Tenant</code> interface represents a tenant which may be used to
+ * further customize request and other processing.
+ * <p>
+ * This interface is intended to be implemented by the implementor of the
+ * {@link TenantProvider} interface to be returned for it tenant accessor
+ * methods.
+ */
+public interface Tenant {
+
+	/**
+	 * The name of the {@link #getProperty(String) property} whose string
+	 * representation is used as this tenant's {@link #getName() name} (value is
+	 * "sling.tenant.name").
+	 * 
+	 * @see #getName()
+	 * @see #getProperty(String)
+	 */
+	String PROP_NAME = "tenant.name";
+
+	/**
+	 * The name of the {@link #getProperty(String) property} whose string
+	 * representation is used as this tenant's {@link #getDescription()
+	 * description} (value is "sling.tenant.description").
+	 * 
+	 * @see #getDescription()
+	 * @see #getProperty(String)
+	 */
+	String PROP_DESCRIPTION = "tenant.description";
+
+	/**
+	 * Returns the unique identifier of this tenant.
+	 * <p>
+	 * The tenant identifier has not predefined mapping to a property and may be
+	 * generated automatically by the TenantProvider.
+	 */
+	String getId();
+
+	/**
+	 * Returns the name of the tenant. This is a short name for quickly
+	 * identifying this tenant. This name is not required to be globally unique.
+	 * <p>
+	 * The name of the tenant is the string representation of the
+	 * {@link #PROP_NAME} property.
+	 */
+	String getName();
+
+	/**
+	 * Returns a human readable description of this tenant.
+	 * <p>
+	 * The description of the tenant is the string representation of the
+	 * {@link #PROP_DESCRIPTION} property.
+	 */
+	String getDescription();
+
+	/**
+	 * Returns the named property or <code>null</code> if no such property
+	 * exists or if the property value itself is <code>null</code>.
+	 */
+	Object getProperty(String name);
+
+	/**
+	 * Returns the named property converted to the requested type or
+	 * <code>null</code> if the property does not exist, the property value
+	 * itself is <code>null</code> or cannot be converted to the requested type.
+	 */
+	<Type> Type getProperty(String name, Type type);
+
+	/**
+	 * Returns an iterator or String values representing the names of defined
+	 * properties of this tenant.
+	 */
+	Iterator<String> getPropertyNames();
+}
diff --git a/src/main/java/org/apache/sling/tenant/TenantProvider.java b/src/main/java/org/apache/sling/tenant/TenantProvider.java
new file mode 100644
index 0000000..193804b
--- /dev/null
+++ b/src/main/java/org/apache/sling/tenant/TenantProvider.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.tenant;
+
+import java.util.Iterator;
+
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * The <code>TenantProvider</code> defines the service interface of for a sevice
+ * which may be asked for {@link Tenant tenant instances}.
+ * <p>
+ * For now this provider interface provides access to a tenant applying to a
+ * particular request as well as to all tenants known to this provider.
+ */
+public interface TenantProvider {
+
+    /**
+     * Returns the {@link Tenant} with the given <code>tenantId</code> or
+     * <code>null</code> if no such tenant exists.
+     */
+    Tenant getTenant(String tenantId);
+
+    /**
+     * Returns an iterator of all {@link Tenant tenants} known to this provider.
+     * If no tenants are known the iterator is empty.
+     */
+    Iterator<Tenant> getTenants();
+
+    /**
+     * Returns an iterator of {@link Tenant tenants} matching the given
+     * <code>tenantFilter</code>.
+     * <p>
+     * The <code>tenantFilter</code> is a valid OSGi filter string as defined in
+     * Section 3.2.6, Filter Syntax, of the OSGi Core Specification, Release 4.
+     * <p>
+     * If no tenants match the <code>tenantFilter</code> or the
+     * <code>tenantFilter</code> is not a valid filter string the iterator is
+     * empty.
+     *
+     * @throws InvalidSyntaxException if filter syntax is invalid
+     */
+    Iterator<Tenant> getTenants(String tenantFilter) throws InvalidSyntaxException;
+}
diff --git a/src/main/java/org/apache/sling/tenant/internal/TenantAdapterFactory.java b/src/main/java/org/apache/sling/tenant/internal/TenantAdapterFactory.java
new file mode 100644
index 0000000..4b364c7
--- /dev/null
+++ b/src/main/java/org/apache/sling/tenant/internal/TenantAdapterFactory.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.tenant.internal;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.sling.api.adapter.AdapterFactory;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.tenant.Tenant;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * JCR based tenant adapter factory, that adapts <code>ResourceResolver</code>
+ * and <code>Resource</code> to <code>Tenant</code>.
+ *
+ * It tries to resolve the tenant based on logged in user by looking at the user
+ * home path i.e. /home/users/tenant1/a/admin
+ *
+ * For resource, it tries to resolve Tenant using resource path.
+ *
+ */
+class TenantAdapterFactory implements AdapterFactory {
+
+	private final Logger log = LoggerFactory.getLogger(getClass());
+
+	 static final Class<ResourceResolver> RESOURCERESOLVER_CLASS = ResourceResolver.class;
+	private static final Class<Resource> RESOURCE_CLASS = Resource.class;
+	private static final Class<Tenant> TENANT_CLASS = Tenant.class;
+
+	private final TenantProviderImpl tenantProvider;
+
+	private final ServiceRegistration<?> service;
+
+	TenantAdapterFactory(final BundleContext bundleContext, final TenantProviderImpl tenantProvider) {
+	    this.tenantProvider = tenantProvider;
+
+	    Dictionary<String, Object> props = new Hashtable<String, Object>();
+	    props.put(Constants.SERVICE_DESCRIPTION, "Apache Sling JCR Tenant Adapter");
+	    props.put(AdapterFactory.ADAPTER_CLASSES, new String[]{ TENANT_CLASS.getName() });
+	    props.put(AdapterFactory.ADAPTABLE_CLASSES, new String[] { RESOURCERESOLVER_CLASS
+	            .getName(), RESOURCE_CLASS.getName() });
+
+	    this.service = bundleContext.registerService(AdapterFactory.SERVICE_NAME, this, props);
+    }
+
+	void dispose() {
+	    if (this.service != null) {
+	        this.service.unregister();
+        }
+	}
+
+	// ---------- AdapterFactory -----------------------------------------------
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see
+	 * org.apache.sling.api.adapter.AdapterFactory#getAdapter(java.lang.Object,
+	 * java.lang.Class)
+	 */
+	public <AdapterType> AdapterType getAdapter(Object adaptable,
+			Class<AdapterType> type) {
+		if (adaptable instanceof ResourceResolver) {
+			return getAdapter(
+					((ResourceResolver) adaptable).adaptTo(Session.class), type);
+		}
+
+		if (adaptable instanceof Resource) {
+			return getAdapter(((Resource) adaptable).getPath(), type);
+		}
+
+		log.warn("Unable to handle adaptable {}", adaptable.getClass()
+				.getName());
+		return null;
+	}
+
+	private <AdapterType> AdapterType getAdapter(Session session,
+			Class<AdapterType> type) {
+		String userID = session.getUserID();
+
+		JackrabbitSession jrSession = (JackrabbitSession) session;
+		try {
+			Authorizable authorizable = jrSession.getUserManager()
+					.getAuthorizable(userID);
+			String userHome = authorizable.getPath();
+
+			// tries to get tenant information from user home
+			// i.e. /home/users/tenant1/a/admin
+			return getAdapter(userHome, type);
+		} catch (Exception e) {
+			log.error("can not get user from session", e);
+		}
+		log.debug("Unable to adapt to resource of type {}", type.getName());
+		return null;
+	}
+
+	@SuppressWarnings("unchecked")
+	private <AdapterType> AdapterType getAdapter(String path,
+			Class<AdapterType> type) {
+		if (type == TENANT_CLASS) {
+			Tenant tenant = tenantProvider.resolveTenantByPath(path);
+
+			if (tenant != null) {
+				return (AdapterType) tenant;
+			}
+
+		}
+		log.debug("Unable to adapt to resource of type {}", type.getName());
+		return null;
+	}
+
+}
diff --git a/src/main/java/org/apache/sling/tenant/internal/TenantImpl.java b/src/main/java/org/apache/sling/tenant/internal/TenantImpl.java
new file mode 100644
index 0000000..8ebb946
--- /dev/null
+++ b/src/main/java/org/apache/sling/tenant/internal/TenantImpl.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.tenant.internal;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.tenant.Tenant;
+
+/**
+ * A resource backed tenant implementation.
+ */
+class TenantImpl implements Tenant {
+    private String id;
+
+    private ValueMap vm;
+
+    TenantImpl(Resource resource) {
+        this.id = resource.getName();
+        // vm = ResourceUtil.getValueMap(resource);
+        // create local detached value map
+        vm = getLocalValueMap(resource);
+    }
+
+    private ValueMap getLocalValueMap(Resource resource) {
+        ValueMap jcrVM = ResourceUtil.getValueMap(resource);
+
+        Map<String, Object> localMap = new HashMap<String, Object>();
+        // copy all items to local map
+        localMap.putAll(jcrVM);
+
+        // decoarate it as value map
+        ValueMapDecorator localVM = new ValueMapDecorator(localMap);
+
+        return localVM;
+
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getName() {
+        return vm.get(Tenant.PROP_NAME, "");
+    }
+
+    public String getDescription() {
+        return vm.get(Tenant.PROP_DESCRIPTION, "");
+    }
+
+    public Object getProperty(String name) {
+        return vm.get(name);
+    }
+
+    public <Type> Type getProperty(String name, Type type) {
+        return vm.get(name, type);
+    }
+
+    public Iterator<String> getPropertyNames() {
+        return vm.keySet().iterator();
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/tenant/internal/TenantProviderImpl.java b/src/main/java/org/apache/sling/tenant/internal/TenantProviderImpl.java
new file mode 100644
index 0000000..92a6e34
--- /dev/null
+++ b/src/main/java/org/apache/sling/tenant/internal/TenantProviderImpl.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.tenant.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.tenant.Tenant;
+import org.apache.sling.tenant.TenantProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * JCR Tenant Provider implementation.
+ */
+@Component(
+        metatype = true,
+        label = "Apache Sling JCR Tenant Provider",
+        description = "Service responsible for providing Tenants")
+@Service
+@Properties(value = {
+    @Property(name = Constants.SERVICE_DESCRIPTION, value = "Apache Sling JCR Tenant Provider")
+})
+public class TenantProviderImpl implements TenantProvider {
+    /**
+     * Root path for tenant
+     */
+    private static final String JCR_TENANT_ROOT = "/etc/tenants";
+
+    @Property(value = JCR_TENANT_ROOT, label = "Tenants Root Path", description = "Defines tenants root path")
+    private static final String TENANT_ROOT = "tenant.root";
+
+    private static final String[] DEFAULT_PATH_MATCHER = {};
+
+    @Property(
+            value = {},
+            label = "Tenants Path Matcher",
+            description = "Defines tenants path matcher i.e. /content/sample/([^/]+)/*, used while resolving path to tenant")
+    private static final String TENANT_PATH_MATCHER = "tenant.path.matcher";
+
+    private String[] pathMatchers;
+
+    private List<Pattern> pathPatterns = new ArrayList<Pattern>();
+
+    private String tenantRootPath = JCR_TENANT_ROOT;
+
+    @Reference
+    private ResourceResolverFactory factory;
+
+    private TenantAdapterFactory adapterFactory;
+
+    @Activate
+    private void activate(final BundleContext bundleContext, final Map<String, Object> properties) {
+        this.tenantRootPath = PropertiesUtil.toString(properties.get(TENANT_ROOT), JCR_TENANT_ROOT);
+        this.pathMatchers = PropertiesUtil.toStringArray(properties.get(TENANT_PATH_MATCHER), DEFAULT_PATH_MATCHER);
+
+        this.pathPatterns.clear();
+        for (String matcherStr : this.pathMatchers) {
+            this.pathPatterns.add(Pattern.compile(matcherStr));
+        }
+
+        this.adapterFactory = new TenantAdapterFactory(bundleContext, this);
+    }
+
+    @Deactivate
+    private void deactivate() {
+        if (this.adapterFactory != null) {
+            this.adapterFactory.dispose();
+            this.adapterFactory = null;
+        }
+    }
+
+    public Tenant getTenant(String tenantId) {
+        if (StringUtils.isBlank(tenantId)) {
+            return null;
+        }
+
+        final ResourceResolver adminResolver = getAdminResolver();
+        if (adminResolver != null) {
+            try {
+                Resource tenantRootRes = adminResolver.getResource(tenantRootPath);
+
+                Resource tenantRes = tenantRootRes.getChild(tenantId);
+                if (tenantRes != null) {
+                    return new TenantImpl(tenantRes);
+                }
+            } finally {
+                adminResolver.close();
+            }
+        }
+
+        // in case of some problem
+        return null;
+    }
+
+    public Iterator<Tenant> getTenants() {
+        final ResourceResolver adminResolver = getAdminResolver();
+        if (adminResolver != null) {
+            try {
+                Resource tenantRootRes = adminResolver.getResource(tenantRootPath);
+
+                List<Tenant> tenantList = new ArrayList<Tenant>();
+                Iterator<Resource> tenantResourceList = tenantRootRes.listChildren();
+                while (tenantResourceList.hasNext()) {
+                    Resource tenantRes = tenantResourceList.next();
+                    tenantList.add(new TenantImpl(tenantRes));
+                }
+                return tenantList.iterator();
+            } finally {
+                adminResolver.close();
+            }
+        }
+
+        // in case of some problem return an empty iterator
+        return Collections.<Tenant> emptyList().iterator();
+    }
+
+    public Tenant addTenant(String name, String tenantId) throws PersistenceException {
+        final ResourceResolver adminResolver = getAdminResolver();
+        if (adminResolver != null) {
+            try {
+                Resource tenantRootRes = adminResolver.getResource(tenantRootPath);
+
+                // check if tenantId already exists
+                Resource child = tenantRootRes.getChild(tenantId);
+
+                if (child != null) {
+                    throw new PersistenceException("Tenant already exists with Id " + tenantId);
+                } else {
+                    // create the tenant
+                    Node rootNode = tenantRootRes.adaptTo(Node.class);
+                    Node tenantNode = rootNode.addNode(tenantId);
+                    tenantNode.setProperty(Tenant.PROP_NAME, name);
+                    adminResolver.adaptTo(Session.class).save();
+                    return new TenantImpl(adminResolver.getResource(tenantNode.getPath()));
+                }
+            } catch (RepositoryException e) {
+                throw new PersistenceException("Unexpected RepositoryException while adding tenant", e);
+            } finally {
+                adminResolver.close();
+            }
+        }
+
+        throw new PersistenceException("Cannot create the tenant");
+    }
+
+    public Iterator<Tenant> getTenants(String tenantFilter) throws InvalidSyntaxException {
+        if (StringUtils.isBlank(tenantFilter)) {
+            return null;
+        }
+
+        final ResourceResolver adminResolver = getAdminResolver();
+        if (adminResolver != null) {
+            try {
+                Resource tenantRootRes = adminResolver.getResource(tenantRootPath);
+
+                List<Tenant> tenantList = new ArrayList<Tenant>();
+                Iterator<Resource> tenantResourceList = tenantRootRes.listChildren();
+                while (tenantResourceList.hasNext()) {
+                    Resource tenantRes = tenantResourceList.next();
+                    ValueMap vm = ResourceUtil.getValueMap(tenantRes);
+
+                    Filter filter = FrameworkUtil.createFilter(tenantFilter);
+                    if (filter.matches(vm)) {
+                        TenantImpl tenant = new TenantImpl(tenantRes);
+                        tenantList.add(tenant);
+                    }
+                }
+                return tenantList.iterator();
+            } finally {
+                adminResolver.close();
+            }
+        }
+
+        // in case of some problem return an empty iterator
+        return Collections.<Tenant> emptyList().iterator();
+    }
+
+    /**
+     * Helper for the {@link JcrTenantAdapterFactory} to resolve any resource
+     * path to a tenant.
+     */
+    Tenant resolveTenantByPath(String path) {
+        // find matching path identifier
+        for (Pattern pathPattern : pathPatterns) {
+            Matcher matcher = pathPattern.matcher(path);
+            if (matcher.find()) {
+                // assuming that first group is tenantId in the path, we can
+                // make group number configurable.
+                if (matcher.groupCount() >= 1) {
+                    String tenantId = matcher.group(1);
+                    return getTenant(tenantId);
+                }
+            }
+        }
+        return null;
+    }
+
+    private ResourceResolver getAdminResolver() {
+        try {
+            return factory.getAdministrativeResourceResolver(null);
+        } catch (LoginException le) {
+            // unexpected, thus ignore
+        }
+
+        return null;
+    }
+
+    private void safeClose(ResourceResolver adminResolver) {
+        if (adminResolver != null) {
+            adminResolver.close();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/tenant/package-info.java b/src/main/java/org/apache/sling/tenant/package-info.java
new file mode 100644
index 0000000..7188a49
--- /dev/null
+++ b/src/main/java/org/apache/sling/tenant/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+@Version("1.0")
+@Export(optional = "provide:=true")
+package org.apache.sling.tenant;
+
+import aQute.bnd.annotation.Export;
+import aQute.bnd.annotation.Version;
+

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.