This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-tenant.git
commit 288d7bff00cdb83f043ab15b68a47c558b8226df Author: Felix Meschberger <[email protected]> AuthorDate: Fri Mar 1 08:04:08 2013 +0000 SLING-2710 Define TenantManager API - Add service interface as a provider type (not to be implemented by consumers) - Implement TenantManager in the TenantProviderImpl - Use latest Repository API to leverage repository CRUD - Move tenant resolution for the AdapterFactory to the TenantAdapterFactory (just handing the configuration over on construction). git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1451514 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 10 +- .../org/apache/sling/tenant/TenantManager.java | 131 +++++++ .../tenant/internal/TenantAdapterFactory.java | 31 +- .../sling/tenant/internal/TenantProviderImpl.java | 412 +++++++++++---------- .../tenant/internal/console/WebConsolePlugin.java | 36 +- 5 files changed, 396 insertions(+), 224 deletions(-) diff --git a/pom.xml b/pom.xml index 7b1c7d8..2554d0f 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.api</artifactId> - <version>2.2.0</version> + <version>2.3.0</version> <scope>provided</scope> </dependency> <dependency> @@ -104,15 +104,9 @@ <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> + <version>4.3.1</version><!--$NO-MVN-MAN-VER$--> <scope>provided</scope> </dependency> <dependency> diff --git a/src/main/java/org/apache/sling/tenant/TenantManager.java b/src/main/java/org/apache/sling/tenant/TenantManager.java new file mode 100644 index 0000000..23b1c1a --- /dev/null +++ b/src/main/java/org/apache/sling/tenant/TenantManager.java @@ -0,0 +1,131 @@ +/* + * 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.Map; + +import aQute.bnd.annotation.ProviderType; + +/** + * The <code>TenantManager</code> service interface defines the API that + * administrative tools will use to created, update or remove Tenants. + * <p> + * The implementation will make use of + * {@link org.apache.sling.tenant.spi.TenantCustomizer} services to customize + * management of tenants. + * <p> + * Tenant properties can be created, modified, and removed with the + * {@link #setProperty(Tenant, String, Object)}, + * {@link #setProperties(Tenant, Map)} and + * {@link #removeProperties(Tenant, String...)} methods. Please note that every + * call to any of these methods causes the + * {@link org.apache.sling.tenant.spi.TenantCustomizer} services to be called. + * To limit these calls for multiple changes the + * {@link #setProperties(Tenant, Map)} method should be called. + */ +@ProviderType +public interface TenantManager { + + /** + * Creates a new tenant with the given tenant ID and the initial set of + * properties. + * <p> + * After creating the tenant, the + * {@link org.apache.sling.tenant.spi.TenantCustomizer#setup(Tenant, org.apache.sling.api.resource.ResourceResolver)} + * method is called to allow customizers to configure additional properties. + * <p> + * Before returning the newly created tenant object the data is persisted. + * + * @param tenantId The name of the new tenant. This name must not be + * {@code null} or an empty string. It must also not be the same + * name as that of an existing tenant. + * @param properties An optional map of initial properties. This may be + * {@code null} to not preset any properties. It is recommended, + * though, that this map contain at least the + * {@link Tenant#PROP_NAME} and {@link Tenant#PROP_DESCRIPTION} + * properties. + * @return The newly created {@link Tenant} instance. + * @throws NullPointerException if {@code tenantId} is {@code null}. + * @throws IllegalArgumentException if a tenant with the same + * {@code tentantId} already exists. + */ + Tenant create(String tenantId, Map<String, Object> properties); + + /** + * Sets a single property of the tenant to a new value or removes the + * property if the value is {@code null}. + * <p> + * Before returning the + * {@link org.apache.sling.tenant.spi.TenantCustomizer#setup(Tenant, org.apache.sling.api.resource.ResourceResolver)} + * method is called to allow customizers to configure additional properties. + * + * @param tenant The tenant whose property is to be set or remove. + * @param name The name of the property to set or remove. + * @param value The new value of the property. If this value is {@code null} + * the property is actually removed. + * @throws NullPointerException if {@code tenant} or {@code name} is + * {@code null}. + */ + void setProperty(Tenant tenant, String name, Object value); + + /** + * Sets or removes multiple properties on the tenant. + * <p> + * Before returning the + * {@link org.apache.sling.tenant.spi.TenantCustomizer#setup(Tenant, org.apache.sling.api.resource.ResourceResolver)} + * method is called to allow customizers to configure additional properties. + * + * @param tenant The tenant whose properties are to be modified. + * @param properties The map of properties to set or remove. A property + * whose value is {@code null} is removed from the tenant. + * @throws NullPointerException if {@code tenant} or {@code properties} is + * {@code null}. + */ + void setProperties(Tenant tenant, Map<String, Object> properties); + + /** + * Removes one or more properties from the tenant. + * <p> + * Before returning the + * {@link org.apache.sling.tenant.spi.TenantCustomizer#setup(Tenant, org.apache.sling.api.resource.ResourceResolver)} + * method is called to allow customizers to configure additional properties + * unless the {@code properties} parameter is {@code null} or empty. + * + * @param tenant The tenant from which to remove properties. + * @param properties The list of properties to be removed. If this is + * {@code null} or empty, nothing happens and the + * {@link org.apache.sling.tenant.spi.TenantCustomizer} is not + * called. + * @throws NullPointerException if {@code tenant} is {@code null}. + */ + void removeProperties(Tenant tenant, String... propertyNames); + + /** + * Removes the given tenant. + * <p> + * Before returning the + * {@link org.apache.sling.tenant.spi.TenantCustomizer#remove(Tenant, org.apache.sling.api.resource.ResourceResolver)} + * method is called to allow customizers to implement further cleanup upon + * tenant removal. + * + * @param tenant The tenant to remove. + * @throws NullPointerException if {@code tenant} is {@code null} + */ + void remove(Tenant tenant); +} diff --git a/src/main/java/org/apache/sling/tenant/internal/TenantAdapterFactory.java b/src/main/java/org/apache/sling/tenant/internal/TenantAdapterFactory.java index 4b364c7..691f956 100644 --- a/src/main/java/org/apache/sling/tenant/internal/TenantAdapterFactory.java +++ b/src/main/java/org/apache/sling/tenant/internal/TenantAdapterFactory.java @@ -18,8 +18,12 @@ */ package org.apache.sling.tenant.internal; +import java.util.ArrayList; import java.util.Dictionary; import java.util.Hashtable; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.jcr.Session; @@ -57,9 +61,16 @@ class TenantAdapterFactory implements AdapterFactory { private final ServiceRegistration<?> service; - TenantAdapterFactory(final BundleContext bundleContext, final TenantProviderImpl tenantProvider) { + private final List<Pattern> pathPatterns; + + TenantAdapterFactory(final BundleContext bundleContext, final TenantProviderImpl tenantProvider, final String[] pathMatchers) { this.tenantProvider = tenantProvider; + this.pathPatterns = new ArrayList<Pattern>(); + for (String matcherStr : pathMatchers) { + this.pathPatterns.add(Pattern.compile(matcherStr)); + } + 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() }); @@ -124,7 +135,7 @@ class TenantAdapterFactory implements AdapterFactory { private <AdapterType> AdapterType getAdapter(String path, Class<AdapterType> type) { if (type == TENANT_CLASS) { - Tenant tenant = tenantProvider.resolveTenantByPath(path); + Tenant tenant = resolveTenantByPath(path); if (tenant != null) { return (AdapterType) tenant; @@ -135,4 +146,20 @@ class TenantAdapterFactory implements AdapterFactory { return null; } + private 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 this.tenantProvider.getTenant(tenantId); + } + } + } + return null; + } + } diff --git a/src/main/java/org/apache/sling/tenant/internal/TenantProviderImpl.java b/src/main/java/org/apache/sling/tenant/internal/TenantProviderImpl.java index a45b83f..cc4d3cc 100644 --- a/src/main/java/org/apache/sling/tenant/internal/TenantProviderImpl.java +++ b/src/main/java/org/apache/sling/tenant/internal/TenantProviderImpl.java @@ -21,19 +21,13 @@ package org.apache.sling.tenant.internal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; -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; @@ -44,18 +38,17 @@ import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.ReferencePolicy; import org.apache.felix.scr.annotations.Service; -import org.apache.jackrabbit.commons.JcrUtils; import org.apache.sling.api.resource.LoginException; -import org.apache.sling.api.resource.PersistableValueMap; +import org.apache.sling.api.resource.ModifiableValueMap; 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.commons.osgi.ServiceUtil; import org.apache.sling.tenant.Tenant; +import org.apache.sling.tenant.TenantManager; import org.apache.sling.tenant.TenantProvider; import org.apache.sling.tenant.internal.console.WebConsolePlugin; import org.apache.sling.tenant.spi.TenantCustomizer; @@ -84,7 +77,7 @@ import org.slf4j.LoggerFactory; referenceInterface = TenantCustomizer.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC) -public class TenantProviderImpl implements TenantProvider { +public class TenantProviderImpl implements TenantProvider, TenantManager { /** default log */ private final Logger log = LoggerFactory.getLogger(getClass()); @@ -109,10 +102,6 @@ public class TenantProviderImpl implements TenantProvider { 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 @@ -122,20 +111,10 @@ public class TenantProviderImpl implements TenantProvider { private WebConsolePlugin plugin; - private BundleContext bundleContext; - @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.bundleContext = bundleContext; - - this.pathPatterns.clear(); - for (String matcherStr : this.pathMatchers) { - this.pathPatterns.add(Pattern.compile(matcherStr)); - } - - this.adapterFactory = new TenantAdapterFactory(bundleContext, this); + this.adapterFactory = new TenantAdapterFactory(bundleContext, this, PropertiesUtil.toStringArray(properties.get(TENANT_PATH_MATCHER), DEFAULT_PATH_MATCHER)); this.plugin = new WebConsolePlugin(bundleContext, this); } @@ -152,10 +131,12 @@ public class TenantProviderImpl implements TenantProvider { } } + @SuppressWarnings("unused") private synchronized void bindTenantSetup(TenantCustomizer action, Map<String, Object> config) { registeredTenantHandlers.put(ServiceUtil.getComparableForServiceRanking(config), action); } + @SuppressWarnings("unused") private synchronized void unbindTenantSetup(TenantCustomizer action, Map<String, Object> config) { registeredTenantHandlers.remove(ServiceUtil.getComparableForServiceRanking(config)); } @@ -164,23 +145,14 @@ public class TenantProviderImpl implements TenantProvider { return registeredTenantHandlers.values(); } - 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); + public Tenant getTenant(final String tenantId) { + if (tenantId != null && tenantId.length() > 0) { + return call(new ResourceResolverTask<Tenant>() { + public Tenant call(ResourceResolver resolver) { + Resource tenantRes = getTenantResource(resolver, tenantId); + return (tenantRes != null) ? new TenantImpl(tenantRes) : null; } - } finally { - adminResolver.close(); - } + }); } // in case of some problem @@ -188,208 +160,246 @@ public class TenantProviderImpl implements TenantProvider { } public Iterator<Tenant> getTenants() { - final ResourceResolver adminResolver = getAdminResolver(); - if (adminResolver != null) { + return getTenants(null); + } + + public Iterator<Tenant> getTenants(final String tenantFilter) { + final Filter filter; + if (tenantFilter != null && tenantFilter.length() > 0) { try { - Resource tenantRootRes = adminResolver.getResource(tenantRootPath); - - if (tenantRootRes != null) { - 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(); + filter = FrameworkUtil.createFilter(tenantFilter); + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException(e.getMessage(), e); } + } else { + filter = null; } - // in case of some problem return an empty iterator - return Collections.<Tenant> emptyList().iterator(); - } + Iterator<Tenant> result = call(new ResourceResolverTask<Iterator<Tenant>>() { + public Iterator<Tenant> call(ResourceResolver resolver) { + Resource tenantRootRes = resolver.getResource(tenantRootPath); - /** - * Creates a new tenant (not exposed as part of the api) - * - * @param name - * @param tenantId - * @param description - * @return - * @throws PersistenceException - */ - public Tenant addTenant(String name, String tenantId, String description) throws PersistenceException { - final ResourceResolver adminResolver = getAdminResolver(); - if (adminResolver != null) { - try { - Resource tenantRootRes = adminResolver.getResource(tenantRootPath); - Session adminSession = adminResolver.adaptTo(Session.class); + List<Tenant> tenantList = new ArrayList<Tenant>(); + Iterator<Resource> tenantResourceList = tenantRootRes.listChildren(); + while (tenantResourceList.hasNext()) { + Resource tenantRes = tenantResourceList.next(); - if (tenantRootRes == null) { - // create the root path - JcrUtils.getOrCreateByPath(tenantRootPath, null, adminSession); - tenantRootRes = adminResolver.getResource(tenantRootPath); + if (filter == null || filter.matches(ResourceUtil.getValueMap(tenantRes))) { + TenantImpl tenant = new TenantImpl(tenantRes); + tenantList.add(tenant); + } } + return tenantList.iterator(); + } + }); - // check if tenantId already exists - Resource child = tenantRootRes.getChild(tenantId); + if (result == null) { + // no filter or no resource resolver for calling + result = Collections.<Tenant> emptyList().iterator(); + } + + return result; + } - if (child != null) { - throw new PersistenceException("Tenant already exists with Id " + tenantId); + public Tenant create(final String tenantId, final Map<String, Object> properties) { + return call(new ResourceResolverTask<Tenant>() { + public Tenant call(ResourceResolver adminResolver) { + try { + // create the tenant + Resource tenantRes = createTenantResource(adminResolver, tenantId, properties); + TenantImpl tenant = new TenantImpl(tenantRes); + customizeTenant(tenantRes, tenant); + adminResolver.commit(); + + // refresh tenant instance, as it copies property from + // resource + tenant.loadProperties(tenantRes); + + return tenant; + + } catch (PersistenceException e) { + log.error("create: Failed creating Tenant {}", tenantId, e); + } finally { + adminResolver.close(); } - // create the tenant - Node rootNode = tenantRootRes.adaptTo(Node.class); - Node tenantNode = rootNode.addNode(tenantId); - tenantNode.setProperty(Tenant.PROP_NAME, name); - tenantNode.setProperty(Tenant.PROP_DESCRIPTION, description); - - Resource resource = adminResolver.getResource(tenantNode.getPath()); - Tenant tenant = new TenantImpl(resource); - PersistableValueMap tenantProps = resource.adaptTo(PersistableValueMap.class); - // call tenant setup handler - for (TenantCustomizer ts : getTenantHandlers()) { - try { - Map<String, Object> props = ts.setup(tenant, adminResolver); - if (props != null) { - tenantProps.putAll(props); + // no new tenant in case of problems + return null; + } + }); + } + + public void remove(final Tenant tenant) { + call(new ResourceResolverTask<Void>() { + public Void call(ResourceResolver resolver) { + try { + Resource tenantRes = getTenantResource(resolver, tenant.getId()); + if (tenantRes != null) { + // call tenant setup handler + for (TenantCustomizer ts : getTenantHandlers()) { + try { + ts.remove(tenant, resolver); + } catch (Exception e) { + log.info("removeTenant: Unexpected problem calling TenantCustomizer " + ts, e); + } } - } catch (Exception e) { - log.info("addTenant: Unexpected problem calling TenantCustomizer " + ts, e); + + resolver.delete(tenantRes); + resolver.commit(); } + } catch (PersistenceException e) { + log.error("remove({}): Cannot persist Tenant removal", tenant.getId(), e); } - // save the properties - tenantProps.save(); - - // save the session - adminSession.save(); - // refersh tenant instance, as it copies property from - // resource - tenant = new TenantImpl(resource); - return tenant; - - } catch (RepositoryException e) { - throw new PersistenceException("Unexpected RepositoryException while adding tenant", e); - } finally { - adminResolver.close(); - } - } - throw new PersistenceException("Cannot create the tenant"); + return null; + } + }); } - /** - * Removes the tenant (not exposed as part of the api) - * - * @param tenantId tenant identifier - * @return - * @throws PersistenceException - */ - public void removeTenant(String tenantId) throws PersistenceException { - final ResourceResolver adminResolver = getAdminResolver(); - if (adminResolver != null) { - try { - Resource tenantRootRes = adminResolver.getResource(tenantRootPath); - - if (tenantRootRes == null) { - // if tenant home is null just return - return; + public void setProperty(final Tenant tenant, final String name, final Object value) { + updateProperties(tenant, new PropertiesUpdater() { + public void update(ModifiableValueMap properties) { + if (value != null) { + properties.put(name, value); + } else { + properties.remove(name); } + } + }); + } - // check if tenantId already exists - Resource tenantRes = tenantRootRes.getChild(tenantId); - - if (tenantRes != null) { - Node tenantNode = tenantRes.adaptTo(Node.class); - Tenant tenant = new TenantImpl(tenantRes); - // call tenant setup handler - for (TenantCustomizer ts : getTenantHandlers()) { - try { - ts.remove(tenant, adminResolver); - } catch (Exception e) { - log.info("removeTenant: Unexpected problem calling TenantCustomizer " + ts, e); - } + public void setProperties(final Tenant tenant, final Map<String, Object> properties) { + updateProperties(tenant, new PropertiesUpdater() { + public void update(ModifiableValueMap vm) { + for (Entry<String, Object> entry : properties.entrySet()) { + if (entry.getValue() != null) { + vm.put(entry.getKey(), entry.getValue()); + } else { + vm.remove(entry.getKey()); } - - tenantNode.remove(); - adminResolver.adaptTo(Session.class).save(); - return; } - // if there was no tenant found, just return - return; - } catch (RepositoryException e) { - throw new PersistenceException("Unexpected RepositoryException while removing tenant", e); - } finally { - adminResolver.close(); } - } + }); + } - throw new PersistenceException("Cannot remove the tenant"); + public void removeProperties(final Tenant tenant, final String... propertyNames) { + updateProperties(tenant, new PropertiesUpdater() { + public void update(ModifiableValueMap properties) { + for (String name : propertyNames) { + properties.remove(name); + } + } + }); } - public Iterator<Tenant> getTenants(String tenantFilter) { - if (StringUtils.isBlank(tenantFilter)) { - return null; + @SuppressWarnings("serial") + private Resource createTenantResource(final ResourceResolver resolver, final String tenantId, + final Map<String, Object> properties) throws PersistenceException { + + // check for duplicate first + if (getTenantResource(resolver, tenantId) != null) { + throw new PersistenceException("Tenant '" + tenantId + "' already exists"); } - final ResourceResolver adminResolver = getAdminResolver(); - if (adminResolver != null) { - try { - Resource tenantRootRes = adminResolver.getResource(tenantRootPath); + Resource tenantRoot = resolver.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); + if (tenantRoot == null) { + Resource current = resolver.getResource("/"); + if (current == null) { + throw new PersistenceException("Cannot get root Resource"); + } - Filter filter = FrameworkUtil.createFilter(tenantFilter); - if (filter.matches(vm)) { - TenantImpl tenant = new TenantImpl(tenantRes); - tenantList.add(tenant); - } + String[] segments = this.tenantRootPath.split("/"); + for (String segment : segments) { + Resource child = current.getChild(segment); + if (child == null) { + child = resolver.create(current, segment, new HashMap<String, Object>() { + { + put("jcr:primaryType", "sling:Folder"); + } + }); } - return tenantList.iterator(); - } catch (InvalidSyntaxException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } finally { - adminResolver.close(); } + + tenantRoot = current; } - // in case of some problem return an empty iterator - return Collections.<Tenant> emptyList().iterator(); + return resolver.create(tenantRoot, tenantId, properties); } - /** - * 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); + private Resource getTenantResource(final ResourceResolver resolver, final String tenantId) { + return resolver.getResource(tenantRootPath + "/" + tenantId); + } + + private void customizeTenant(final Resource tenantRes, final Tenant tenant) { + + // call tenant setup handler + Map<String, Object> tenantProps = tenantRes.adaptTo(ModifiableValueMap.class); + if (tenantProps == null) { + log.warn( + "create({}): Cannot get ModifiableValueMap for new tenant; will not store changed properties of TenantCustomizers", + tenant.getId()); + tenantProps = new HashMap<String, Object>(); + } + + for (TenantCustomizer ts : getTenantHandlers()) { + try { + Map<String, Object> props = ts.setup(tenant, tenantRes.getResourceResolver()); + if (props != null) { + tenantProps.putAll(props); } + } catch (Exception e) { + log.info("addTenant: Unexpected problem calling TenantCustomizer " + ts, e); } } - return null; } - private ResourceResolver getAdminResolver() { + private <T> T call(ResourceResolverTask<T> task) { + ResourceResolver resolver = null; + T result = null; + try { - return factory.getAdministrativeResourceResolver(null); + resolver = factory.getAdministrativeResourceResolver(null); + result = task.call(resolver); } catch (LoginException le) { // unexpected, thus ignore + } finally { + if (resolver != null) { + resolver.close(); + } } - return null; + return result; + } + + private void updateProperties(final Tenant tenant, final PropertiesUpdater updater) { + call(new ResourceResolverTask<Void>() { + public Void call(ResourceResolver resolver) { + try { + Resource tenantRes = getTenantResource(resolver, tenant.getId()); + if (tenantRes != null) { + updater.update(tenantRes.adaptTo(ModifiableValueMap.class)); + customizeTenant(tenantRes, tenant); + resolver.commit(); + + if (tenant instanceof TenantImpl) { + ((TenantImpl) tenant).loadProperties(tenantRes); + } + } + } catch (PersistenceException pe) { + log.error("setProperty({}): Cannot persist Tenant removal", tenant.getId(), pe); + } + + return null; + } + }); + } + + private static interface ResourceResolverTask<T> { + T call(ResourceResolver resolver); + } + + private static interface PropertiesUpdater { + void update(ModifiableValueMap properties); } } diff --git a/src/main/java/org/apache/sling/tenant/internal/console/WebConsolePlugin.java b/src/main/java/org/apache/sling/tenant/internal/console/WebConsolePlugin.java index f3eb471..fb7871e 100644 --- a/src/main/java/org/apache/sling/tenant/internal/console/WebConsolePlugin.java +++ b/src/main/java/org/apache/sling/tenant/internal/console/WebConsolePlugin.java @@ -21,6 +21,7 @@ package org.apache.sling.tenant.internal.console; import java.io.IOException; import java.io.PrintWriter; import java.util.Dictionary; +import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; @@ -29,7 +30,6 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.sling.api.resource.PersistenceException; import org.apache.sling.tenant.Tenant; import org.apache.sling.tenant.internal.TenantProviderImpl; import org.osgi.framework.BundleContext; @@ -96,11 +96,11 @@ public class WebConsolePlugin extends HttpServlet { String msg = null; final String cmd = req.getParameter("action"); if ("create".equals(cmd)) { - try { - Tenant t = this.createTenant(req); + Tenant t = this.createTenant(req); + if (t != null) { msg = String.format("Created Tenant %s (%s)", t.getName(), t.getDescription()); - } catch (PersistenceException pe) { - msg = "Cannot create tenant: " + pe.getMessage(); + } else { + msg = "Cannot create tenant"; } } else if ("remove".equals(cmd)) { this.removeTenant(req); @@ -119,9 +119,13 @@ public class WebConsolePlugin extends HttpServlet { resp.sendRedirect(redirectTo); } - private void removeTenant(HttpServletRequest request) throws PersistenceException { - String tenantId = request.getParameter(REQ_PRM_TENANT_ID); - tenantProvider.removeTenant(tenantId); + private void removeTenant(HttpServletRequest request) { + final String tenantId = request.getParameter(REQ_PRM_TENANT_ID); + final Tenant tenant = this.tenantProvider.getTenant(tenantId); + + if (tenant != null) { + this.tenantProvider.remove(tenant); + } } private void printForm(final PrintWriter pw, final Tenant t, final String buttonLabel, final String cmd) { @@ -129,12 +133,18 @@ public class WebConsolePlugin extends HttpServlet { + "%s</button>", cmd, (t != null ? t.getId() : ""), buttonLabel); } - private Tenant createTenant(HttpServletRequest request) throws PersistenceException { - String tenantName = request.getParameter(REQ_PRM_TENANT_NAME); - String tenantId = request.getParameter(REQ_PRM_TENANT_ID); - String tenantDesc = request.getParameter(REQ_PRM_TENANT_DESC); + @SuppressWarnings("serial") + private Tenant createTenant(HttpServletRequest request) { + final String tenantName = request.getParameter(REQ_PRM_TENANT_NAME); + final String tenantId = request.getParameter(REQ_PRM_TENANT_ID); + final String tenantDesc = request.getParameter(REQ_PRM_TENANT_DESC); - return tenantProvider.addTenant(tenantName, tenantId, tenantDesc); + return tenantProvider.create(tenantId, new HashMap<String, Object>() { + { + put(Tenant.PROP_NAME, tenantName); + put(Tenant.PROP_DESCRIPTION, tenantDesc); + } + }); } @Override -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
