Package org.apache.sling.rewriter.impl

Source Code of org.apache.sling.rewriter.impl.FactoryCache$TransformerFactoryServiceTracker

/*
* 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.rewriter.impl;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.sling.commons.osgi.OsgiUtil;
import org.apache.sling.rewriter.Generator;
import org.apache.sling.rewriter.GeneratorFactory;
import org.apache.sling.rewriter.ProcessingContext;
import org.apache.sling.rewriter.Processor;
import org.apache.sling.rewriter.ProcessorConfiguration;
import org.apache.sling.rewriter.ProcessorFactory;
import org.apache.sling.rewriter.Serializer;
import org.apache.sling.rewriter.SerializerFactory;
import org.apache.sling.rewriter.Transformer;
import org.apache.sling.rewriter.TransformerFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This is an utility class for accessing the various pipeline components.
* it also acts like a cache for the factories.
*/
public class FactoryCache {

    /** The required property containing the component type. */
    private static final String PROPERTY_TYPE = "pipeline.type";

    /** The optional property for the pipeline mode (global) */
    private static final String PROPERTY_MODE = "pipeline.mode";

    /** The global mode. */
    private static final String MODE_GLOBAL = "global";

    /** The optional property for the paths the component should apply to */
    private static final String PROPERTY_PATHS = "pipeline.paths";

    /** The optional property for the extensions the component should apply to */
    private static final String PROPERTY_EXTENSIONS = "pipeline.extensions";

    /** The optional property for the content types the component should apply to */
    private static final String PROPERTY_CONTENT_TYPES = "pipeline.contentTypes";

    /** The optional property for the selectors the component should apply to */
    private static final String PROPERTY_SELECTORS = "pipeline.selectors";

    /** The optional property for the resource types the component should apply to */
    private static final String PROPERTY_RESOURCE_TYPES = "pipeline.resourceTypes";

    /** The logger. */
    private static final Logger LOGGER = LoggerFactory.getLogger(FactoryCache.class);

    /** The tracker for generator factories. */
    private final HashingServiceTrackerCustomizer<GeneratorFactory> generatorTracker;

    /** The tracker for serializers factories. */
    private final HashingServiceTrackerCustomizer<SerializerFactory> serializerTracker;

    /** The tracker for transformer factories. */
    private final TransformerFactoryServiceTracker<TransformerFactory> transformerTracker;

    /** The tracker for processor factories. */
    private final HashingServiceTrackerCustomizer<ProcessorFactory> processorTracker;

    public FactoryCache(final BundleContext context)
    throws InvalidSyntaxException {
        this.generatorTracker = new HashingServiceTrackerCustomizer<GeneratorFactory>(context,
                GeneratorFactory.class.getName());
        this.serializerTracker = new HashingServiceTrackerCustomizer<SerializerFactory>(context,
                SerializerFactory.class.getName());
        this.transformerTracker = new TransformerFactoryServiceTracker<TransformerFactory>(context,
                TransformerFactory.class.getName());
        this.processorTracker = new HashingServiceTrackerCustomizer<ProcessorFactory>(context,
                ProcessorFactory.class.getName());
    }

    /**
     * Start tracking
     */
    public void start() {
        this.generatorTracker.open();
        this.serializerTracker.open();
        this.transformerTracker.open();
        this.processorTracker.open();
    }

    /**
     * Stop tracking
     */
    public void stop() {
        this.generatorTracker.close();
        this.serializerTracker.close();
        this.transformerTracker.close();
        this.processorTracker.close();
    }

    /**
     * Get the generator of the given type.
     * @param type The generator type.
     * @return The generator or null if the generator is not available.
     */
    public Generator getGenerator(final String type) {
        final GeneratorFactory factory = this.generatorTracker.getFactory(type);
        if ( factory == null ) {
            LOGGER.debug("Requested generator factory for type '{}' not found.", type);
            return null;
        }
        return factory.createGenerator();
    }

    /**
     * Get the serializer of the given type.
     * @param type The serializer type.
     * @return The serializer or null if the serializer is not available.
     */
    public Serializer getSerializer(final String type) {
        final SerializerFactory factory = this.serializerTracker.getFactory(type);
        if ( factory == null ) {
            LOGGER.debug("Requested serializer factory for type '{}' not found.", type);
            return null;
        }
        return factory.createSerializer();
    }

    /**
     * Get the transformer of the given type.
     * @param type The transformer type.
     * @return The transformer or null if the transformer is not available.
     */
    public Transformer getTransformer(final String type) {
        final TransformerFactory factory = this.transformerTracker.getFactory(type);
        if ( factory == null ) {
            LOGGER.debug("Requested transformer factory for type '{}' not found.", type);
            return null;
        }
        return factory.createTransformer();
    }

    /**
     * Get the processor of the given type.
     * @param type The processor type.
     * @return The processor or null if the processor is not available.
     */
    public Processor getProcessor(final String type) {
        final ProcessorFactory factory = this.processorTracker.getFactory(type);
        if ( factory == null ) {
            LOGGER.debug("Requested processor factory for type '{}' not found.", type);
            return null;
        }
        return factory.createProcessor();
    }

    private static final Transformer[] EMPTY_ARRAY = new Transformer[0];
    private static final Transformer[][] EMPTY_DOUBLE_ARRAY = new Transformer[][] {EMPTY_ARRAY, EMPTY_ARRAY};

    /**
     * Lookup all global transformers that apply to the current request and return
     * the transformer instances in two arrays.
     * The first array contains all pre transformers and the second one contains
     * all post transformers.
     * @param context The current processing context.
     */
    public Transformer[][] getGlobalTransformers(final ProcessingContext context) {
        final TransformerFactory[][] factories = this.transformerTracker.getGlobalTransformerFactories(context);
        return createTransformers(factories);
    }

    /**
     * Create new instances from the factories
     * @param factories The transformer factories
     * @return The transformer instances
     */
    private Transformer[][] createTransformers(final TransformerFactory[][] factories) {
        if ( factories == EMPTY_DOUBLE_ARRAY ) {
            return FactoryCache.EMPTY_DOUBLE_ARRAY;
        }
        final Transformer[][] transformers = new Transformer[2][];
        for(int arrayIndex = 0; arrayIndex < 2; arrayIndex++) {
            int count = factories[arrayIndex].length;
            for(final TransformerFactory factory : factories[arrayIndex]) {
                if ( factory == null ) count--;
            }
            if ( count == 0 ) {
                transformers[arrayIndex] = FactoryCache.EMPTY_ARRAY;
            } else {
                transformers[arrayIndex] = new Transformer[count];
                for(int i=0; i < factories[arrayIndex].length; i++) {
                    final TransformerFactory factory = factories[arrayIndex][i];
                    if ( factory != null ) {
                        transformers[arrayIndex][i] = factory.createTransformer();
                    }
                }
            }
        }

        return transformers;
    }

    /**
     * This service tracker stores all services into a hash map.
     */
    private static class HashingServiceTrackerCustomizer<T> extends ServiceTracker {

        /** The services hashed by their name property. */
        private final Map<String, T> services = new ConcurrentHashMap<String, T>();

        /** The bundle context. */
        protected final BundleContext context;

        public HashingServiceTrackerCustomizer(final BundleContext bc, final String serviceClassName) {
            super(bc, serviceClassName, null);
            this.context = bc;
        }

        public T getFactory(final String type) {
            return services.get(type);
        }

        private String getType(final ServiceReference ref) {
            final String type = (String) ref.getProperty(PROPERTY_TYPE);
            return type;
        }

        /**
         * @see org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
         */
        public Object addingService(final ServiceReference reference) {
            final String type = this.getType(reference);
            @SuppressWarnings("unchecked")
            final T factory = (type == null ? null : (T) this.context.getService(reference));
            if ( factory != null ) {
                if ( LOGGER.isDebugEnabled() ) {
                    LOGGER.debug("Found service {}, type={}.", factory, type);
                }
                this.services.put(type, factory);
            }
            return factory;
        }

        /**
         * @see org.osgi.util.tracker.ServiceTrackerCustomizer#removedService(org.osgi.framework.ServiceReference, java.lang.Object)
         */
        public void removedService(final ServiceReference reference, final Object service) {
            final String type = this.getType(reference);
            if ( type != null ) {
                this.services.remove(type);
                this.context.ungetService(reference);
            }
        }
    }

    private static final class TransformerFactoryServiceTracker<T> extends HashingServiceTrackerCustomizer<T> {

        private String getMode(final ServiceReference ref) {
            final String mode = (String) ref.getProperty(PROPERTY_MODE);
            return mode;
        }

        private boolean isGlobal(final ServiceReference ref) {
            return MODE_GLOBAL.equalsIgnoreCase(this.getMode(ref));
        }

        public static final TransformerFactoryEntry[] EMPTY_ENTRY_ARRAY = new TransformerFactoryEntry[0];
        public static final TransformerFactoryEntry[][] EMPTY_DOUBLE_ENTRY_ARRAY = new TransformerFactoryEntry[][] {EMPTY_ENTRY_ARRAY, EMPTY_ENTRY_ARRAY};

        public static final TransformerFactory[] EMPTY_FACTORY_ARRAY = new TransformerFactory[0];
        public static final TransformerFactory[][] EMPTY_DOUBLE_FACTORY_ARRAY = new TransformerFactory[][] {EMPTY_FACTORY_ARRAY, EMPTY_FACTORY_ARRAY};

        private TransformerFactoryEntry[][] cached = EMPTY_DOUBLE_ENTRY_ARRAY;

        /** flag for cache. */
        private boolean cacheIsValid = true;

        public TransformerFactoryServiceTracker(final BundleContext bc, final String serviceClassName) {
            super(bc, serviceClassName);
        }

        /**
         * @see org.osgi.util.tracker.ServiceTracker#addingService(org.osgi.framework.ServiceReference)
         */
        public Object addingService(ServiceReference reference) {
            final boolean isGlobal = isGlobal(reference);
            if ( isGlobal ) {
                this.cacheIsValid = false;
            }
            Object obj = super.addingService(reference);
            if ( obj == null && isGlobal ) {
                obj = this.context.getService(reference);
            }
            return obj;
        }

        /**
         * @see org.osgi.util.tracker.ServiceTracker#removedService(org.osgi.framework.ServiceReference, java.lang.Object)
         */
        public void removedService(ServiceReference reference, Object service) {
            if ( isGlobal(reference) ) {
                this.cacheIsValid = false;
            }
            super.removedService(reference, service);
        }

        /**
         * Get all global transformer factories.
         * @return Two arrays of transformer factories
         */
        public TransformerFactoryEntry[][] getGlobalTransformerFactoryEntries() {
            if ( !this.cacheIsValid ) {
                synchronized ( this ) {
                    if ( !this.cacheIsValid ) {
                        final ServiceReference[] refs = this.getServiceReferences();
                        if ( refs == null || refs.length == 0 ) {
                            this.cached = EMPTY_DOUBLE_ENTRY_ARRAY;
                        } else {
                            Arrays.sort(refs, ServiceReferenceComparator.INSTANCE);

                            int preCount = 0;
                            int postCount = 0;
                            for(final ServiceReference ref : refs) {
                                if ( isGlobal(ref) ) {
                                    final Object r = ref.getProperty(Constants.SERVICE_RANKING);
                                    int ranking = (r instanceof Integer ? (Integer)r : 0);
                                    if ( ranking < 0 ) {
                                        preCount++;
                                    } else {
                                        postCount++;
                                    }
                                }
                            }
                            final TransformerFactoryEntry[][] globalFactories = new TransformerFactoryEntry[2][];
                            if ( preCount == 0 ) {
                                globalFactories[0] = EMPTY_ENTRY_ARRAY;
                            } else {
                                globalFactories[0] = new TransformerFactoryEntry[preCount];
                            }
                            if ( postCount == 0) {
                                globalFactories[1] = EMPTY_ENTRY_ARRAY;
                            } else {
                                globalFactories[1] = new TransformerFactoryEntry[postCount];
                            }
                            int index = 0;
                            for(final ServiceReference ref : refs) {
                                if ( isGlobal(ref) ) {
                                    if ( index < preCount ) {
                                        globalFactories[0][index] = new TransformerFactoryEntry((TransformerFactory) this.getService(ref), ref);
                                    } else {
                                        globalFactories[1][index - preCount] = new TransformerFactoryEntry((TransformerFactory) this.getService(ref), ref);
                                    }
                                    index++;
                                }
                            }
                            this.cached = globalFactories;
                        }
                    }
                    this.cacheIsValid = true;
                }
            }

            return this.cached;
        }

        /**
         * Get all global transformer factories that apply to the current request.
         * @param context The current processing context.
         * @return Two arrays containing the transformer factories.
         */
        public TransformerFactory[][] getGlobalTransformerFactories(final ProcessingContext context) {
            final TransformerFactoryEntry[][] globalFactoryEntries = this.getGlobalTransformerFactoryEntries();
            // quick check
            if ( globalFactoryEntries == EMPTY_DOUBLE_ENTRY_ARRAY ) {
                return EMPTY_DOUBLE_FACTORY_ARRAY;
            }
            final TransformerFactory[][] factories = new TransformerFactory[2][];
            for(int i=0; i<2; i++) {
                if ( globalFactoryEntries[i] == EMPTY_ENTRY_ARRAY ) {
                    factories[i] = EMPTY_FACTORY_ARRAY;
                } else {
                    factories[i] = new TransformerFactory[globalFactoryEntries[i].length];
                    for(int m=0; m<globalFactoryEntries[i].length; m++) {
                        final TransformerFactoryEntry entry = globalFactoryEntries[i][m];
                        if ( entry.match(context) ) {
                            factories[i][m] = entry.factory;
                        }
                    }
                }
            }
            return factories;
        }
    }

    /**
     * Comparator for service references.
     */
    private static final class ServiceReferenceComparator implements Comparator<ServiceReference> {
        public static ServiceReferenceComparator INSTANCE = new ServiceReferenceComparator();

        public int compare(ServiceReference o1, ServiceReference o2) {
            return o1.compareTo(o2);
        }
    }

    private static final class TransformerFactoryEntry {
        public final TransformerFactory factory;

        private final ProcessorConfiguration configuration;

        public TransformerFactoryEntry(final TransformerFactory factory, final ServiceReference ref) {
            this.factory = factory;
            final String[] paths = OsgiUtil.toStringArray(ref.getProperty(PROPERTY_PATHS), null);
            final String[] extensions = OsgiUtil.toStringArray(ref.getProperty(PROPERTY_EXTENSIONS), null);
            final String[] contentTypes = OsgiUtil.toStringArray(ref.getProperty(PROPERTY_CONTENT_TYPES), null);
            final String[] resourceTypes = OsgiUtil.toStringArray(ref.getProperty(PROPERTY_RESOURCE_TYPES), null);
            final String[] selectors = OsgiUtil.toStringArray(ref.getProperty(PROPERTY_SELECTORS), null);
            final boolean noCheckRequired = (paths == null || paths.length == 0) &&
                                   (extensions == null || extensions.length == 0) &&
                                   (contentTypes == null || contentTypes.length == 0) &&
                                   (resourceTypes == null || resourceTypes.length == 0) &&
                                   (selectors == null || selectors.length == 0);
            if ( !noCheckRequired ) {
                this.configuration = new ProcessorConfigurationImpl(contentTypes, paths, extensions, resourceTypes, selectors);
            } else {
                this.configuration = null;
            }
        }

        public boolean match(final ProcessingContext context) {
            if ( configuration == null ) {
                return true;
            }
            return configuration.match(context);
        }
    }
}
TOP

Related Classes of org.apache.sling.rewriter.impl.FactoryCache$TransformerFactoryServiceTracker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.