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>.