de.acosix.alfresco.mtsupport.repo.subsystems.TenantAwareSubsystemPlaceholderConfigurer.java Source code

Java tutorial

Introduction

Here is the source code for de.acosix.alfresco.mtsupport.repo.subsystems.TenantAwareSubsystemPlaceholderConfigurer.java

Source

/*
 * Copyright 2016 Acosix GmbH
 *
 * Licensed 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 de.acosix.alfresco.mtsupport.repo.subsystems;

import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionVisitor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.util.StringValueResolver;

import de.acosix.alfresco.mtsupport.repo.beans.TenantBeanUtils;

/**
 *
 * @author Axel Faust, <a href="http://acosix.de">Acosix GmbH</a>
 */
public class TenantAwareSubsystemPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

    // since this bean is likely to be used inside of subsystems there is a chance the logging framework has already been configured
    private static final Logger LOGGER = LoggerFactory.getLogger(TenantAwareSubsystemPlaceholderConfigurer.class);

    protected static final ThreadLocal<String> TENANT_CONTEXT = new ThreadLocal<>();

    protected String beanName;

    protected BeanFactory beanFactory;

    /**
     *
     * {@inheritDoc}
     */
    @Override
    public void setBeanName(final String beanName) {
        super.setBeanName(beanName);
        this.beanName = beanName;
    }

    /**
     *
     * {@inheritDoc}
     */
    @Override
    public void setBeanFactory(final BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);
        this.beanFactory = beanFactory;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void doProcessProperties(final ConfigurableListableBeanFactory beanFactoryToProcess,
            final StringValueResolver valueResolver) {
        final BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver) {

            /**
             * {@inheritDoc}
             */
            @Override
            protected Object resolveValue(final Object value) {
                Object result = value;

                // TODO Report bug with Spring
                // TypedStringValue may be reused as result of cloneBeanDefinition and thus should not be mutated

                if (value instanceof TypedStringValue) {
                    final TypedStringValue typedStringValue = (TypedStringValue) value;
                    final String stringValue = typedStringValue.getValue();
                    if (stringValue != null) {
                        final String visitedString = this.resolveStringValue(stringValue);
                        if (!stringValue.equals(visitedString)) {
                            result = typedStringValue.hasTargetType()
                                    ? new TypedStringValue(visitedString, typedStringValue.getTargetType())
                                    : new TypedStringValue(visitedString);
                        }
                    }
                } else {
                    result = super.resolveValue(value);
                }

                return result;
            }
        };

        final String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
        for (final String curName : beanNames) {
            // Check that we're not parsing our own bean definition,
            // to avoid failing on unresolvable placeholders in properties file locations.
            if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
                final String tenantDomain;
                if (curName.contains(TenantBeanUtils.TENANT_BEAN_NAME_PATTERN)) {
                    tenantDomain = curName.substring(curName.indexOf(TenantBeanUtils.TENANT_BEAN_NAME_PATTERN)
                            + TenantBeanUtils.TENANT_BEAN_NAME_PATTERN.length());
                    LOGGER.debug("[{}] Processing bean {} for tenant domain {}", this.beanName, curName,
                            tenantDomain);
                } else {
                    LOGGER.debug("[{}] Processing bean {} without tenant domain", this.beanName, curName);
                    tenantDomain = null;
                }

                final BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
                TENANT_CONTEXT.set(tenantDomain);
                try {
                    visitor.visitBeanDefinition(bd);
                } catch (final Exception ex) {
                    throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage());
                } finally {
                    TENANT_CONTEXT.remove();
                }
            }
        }
        LOGGER.debug("[{}] Completed processing all beans", this.beanName);

        // New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
        beanFactoryToProcess.resolveAliases(valueResolver);

        // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
        beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String resolvePlaceholder(final String placeholder, final Properties props,
            final int systemPropertiesMode) {
        String resolved = null;
        if (TenantBeanUtils.TENANT_PLACEHOLDER.equals(placeholder)) {
            resolved = TENANT_CONTEXT.get();
        }

        // placeholder contains the expected placeholder, perform resolution for tenant-specific and generic default variant of placeholder
        else if (TENANT_CONTEXT.get() != null
                && placeholder.contains(TenantBeanUtils.TENANT_PLACEHOLDER_IN_PLACEHOLDER)) {
            final String tenantDomain = TENANT_CONTEXT.get();
            LOGGER.debug("[{}] Processing placeholder {} for tenant domain", this.beanName, placeholder,
                    tenantDomain);

            final StringBuilder tenantReplacementBuilder = new StringBuilder();
            tenantReplacementBuilder.append(".");
            tenantReplacementBuilder.append(tenantDomain);
            tenantReplacementBuilder.append(".");

            final String placeholderWithSpecificTenant = placeholder.replace(
                    TenantBeanUtils.TENANT_PLACEHOLDER_IN_PLACEHOLDER, tenantReplacementBuilder.toString());

            resolved = super.resolvePlaceholder(placeholderWithSpecificTenant, props, systemPropertiesMode);

            if (resolved == null) {
                tenantReplacementBuilder.delete(1, tenantReplacementBuilder.length());

                final String placeholderForDefaultProperty = placeholder.replace(
                        TenantBeanUtils.TENANT_PLACEHOLDER_IN_PLACEHOLDER, tenantReplacementBuilder.toString());

                resolved = super.resolvePlaceholder(placeholderForDefaultProperty, props, systemPropertiesMode);

                if (resolved != null) {
                    LOGGER.debug("[{}] Placeholder {} resolved to value {} from default configuration",
                            this.beanName, placeholder, resolved);
                } else {
                    LOGGER.debug(
                            "[{}] Placeholder {} could not be resolved against tenant or default configuration",
                            this.beanName, placeholder);
                }
            } else {
                LOGGER.debug("[{}] Placeholder {} resolved specific value {} from tenant configuration",
                        this.beanName, placeholder, resolved);
            }
        }

        // fall back
        if (resolved == null) {
            resolved = super.resolvePlaceholder(placeholder, props, systemPropertiesMode);
        }

        return resolved;
    }
}