Package org.apache.sling.jcr.jackrabbit.base.config

Source Code of org.apache.sling.jcr.jackrabbit.base.config.OsgiBeanFactory$DepFinderBeanConfigVisitor

/*
* 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.jcr.jackrabbit.base.config;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.jackrabbit.core.config.BeanConfig;
import org.apache.jackrabbit.core.config.BeanConfigVisitor;
import org.apache.jackrabbit.core.config.BeanFactory;
import org.apache.jackrabbit.core.config.ConfigurationException;
import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
import org.apache.jackrabbit.core.config.SimpleBeanFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;


public class OsgiBeanFactory implements BeanFactory, ServiceTrackerCustomizer {

    private final Logger log = LoggerFactory.getLogger(getClass());

    private final BeanFactory delegate = new SimpleBeanFactory();
    private final BundleContext bundleContext;

    /**
     * Tracker to track all services which are possible Jackrabbit extensions
     */
    private final ServiceTracker tracker;

    /**
     * Set of all interface class instances for which actual instances need to
     * be lookedup from OSGi Service Registry
     */
    private final Set<Class> dependencies = new HashSet<Class>();

    /**
     * Map of className to class instances
     */
    private final Map<String, Class> classNameMapping = new HashMap<String, Class>();

    /**
     * Map of the interface name -> instance where the instance provides an implementation
     * of the given interface
     */
    private final Map<Class, Object> instanceMap = new ConcurrentHashMap<Class, Object>();

    private ServiceRegistration beanFactoryReg;

    public OsgiBeanFactory(BundleContext bundleContext) {
        this.bundleContext = bundleContext;
        Filter filter = null;
        try {
            filter = bundleContext.createFilter("(jackrabbit.extension=true)");
        } catch (InvalidSyntaxException e) {
            //Should not happen
            throw new RuntimeException("Invalid filter", e);
        }
        this.tracker = new ServiceTracker(bundleContext, filter, this);
    }

    public void initialize(final InputSource configSource) throws ConfigurationException {
        determineDependencies(configSource);
        createClassNameMappings();
        tracker.open();
        checkState();
    }

    public void close() {
        if (beanFactoryReg != null) {
            beanFactoryReg.unregister();
            beanFactoryReg = null;
        }
        tracker.close();
        dependencies.clear();
        instanceMap.clear();
        classNameMapping.clear();
    }

    //-----------------------------------------------< BeanFactory >

    public Object newInstance(Class<?> clazz, BeanConfig config) throws ConfigurationException {
        Class targetClass = getClassFromConfig(config);
        if (targetClass.isInterface()) {
            Object o = instanceMap.get(targetClass);
            if (o == null) {
                throw new ConfigurationException("No instance registered for type " + targetClass.getName());
            }
            return o;
        }
        return delegate.newInstance(clazz, config);
    }

    //-----------------------------------------------< ServiceTrackerCustomizer >

    public Object addingService(ServiceReference reference) {
        Object instance = bundleContext.getService(reference);
        Class[] depsProvided = determineProvidedDependencies(reference);
        registerInstance(depsProvided, instance);
        checkState();
        return depsProvided;
    }

    public void modifiedService(ServiceReference serviceReference, Object o) {

    }

    public void removedService(ServiceReference reference, Object o) {
        deregisterInstance((Class[]) o);
        checkState();
        bundleContext.ungetService(reference);
    }

    //------------------------- Callback methods

    /**
     * Callback method invoked when all services required by Jackrabbit are available. Implementing class
     * can use this to manage repository lifecycle. Default implementation registers the BeanFactory
     * instance which would be used by the Repository creator to create the repository
     */
    protected void dependenciesSatisfied() {
        //TODO Review the thread safety aspect
        ServiceRegistration reg = beanFactoryReg;
        if (reg == null) {
            beanFactoryReg = bundleContext.registerService(BeanFactory.class.getName(), this, new Properties());
            log.info("All dependencies are satisfied. Registering the BeanFactory instance");
        }
    }

    /**
     * Callback method invoked when any of the service required by Jackrabbit goes away. Implementing class
     * can use this to manage repository lifecycle. Default implementation de-registers the BeanFactory
     * instance. And repository creator service which then depends on BeanFactory reference would then be notified and
     * can react accordingly
     */
    protected void dependenciesUnSatisfied() {
        ServiceRegistration reg = beanFactoryReg;
        if (reg != null) {
            reg.unregister();
            beanFactoryReg = null;
            log.info("Dependencies unsatisfied. Deregistering the BeanFactory instance");
        }
    }

    private Class getClassFromConfig(BeanConfig config) {
        String cname = config.getClassName();
        try {
            return config.getClassLoader().loadClass(cname);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Could not load class for " + cname, e);
        }
    }

    private void checkState() {
        if (instanceMap.size() == dependencies.size()) {
            dependenciesSatisfied();
        } else {
            dependenciesUnSatisfied();
        }
    }

    private void determineDependencies(final InputSource source) throws ConfigurationException {
        final Properties p = new Properties();
        p.putAll(System.getProperties());
        p.setProperty(RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE, "/fake/path");

        final RepositoryConfigurationParser parser = new RepositoryConfigurationParser(p);
        parser.setConfigVisitor(new DepFinderBeanConfigVisitor());

        try {
            parser.parseRepositoryConfig(source);
        } finally {
            // close source
            final InputStream is = source.getByteStream();
            if (is != null) {
                try { is.close(); } catch (final IOException ignore) {}
            } else {
                final Reader r = source.getCharacterStream();
                if ( r != null ) {
                    try { r.close(); } catch (final IOException ignore) {}
                }
            }
        }

        if (dependencies.isEmpty()) {
            log.info("No dependencies configured. Repository would be created without any OSGi dependency getting injected");
            return;
        }

        log.info("Following dependencies have been determined for the repository {}. Repository would be started " +
                "once all these dependencies have been satisfied", dependencies);
    }

    private void registerInstance(Class[] depsProvided, Object o) {
        for (Class c : depsProvided) {
            instanceMap.put(c, o);
        }
    }

    private void deregisterInstance(Class[] depsProvided) {
        for (Class c : depsProvided) {
            instanceMap.remove(c);
        }
    }

    /**
     * Determines all the dependencies which this ServiceReference can satisfy
     */
    private Class[] determineProvidedDependencies(ServiceReference ref) {
        //Use OBJECTCLASS property from SR as that determines under what classes
        //a given service instance is published
        //Class[] interfaces = o.getClass().getInterfaces();
        String[] interfaces = (String[]) ref.getProperty(Constants.OBJECTCLASS);
        List<Class> depsProvided = new ArrayList<Class>(interfaces.length);
        for (String intf : interfaces) {
            if (classNameMapping.containsKey(intf)) {
                depsProvided.add(classNameMapping.get(intf));
            }
        }
        return depsProvided.toArray(new Class[depsProvided.size()]);
    }

    private void createClassNameMappings() {
        for (Class clazz : dependencies) {
            classNameMapping.put(clazz.getName(), clazz);
        }
    }

    private class DepFinderBeanConfigVisitor implements BeanConfigVisitor {

        public void visit(BeanConfig config) {
            Class clazz = getClassFromConfig(config);
            if (clazz.isInterface()) {
                dependencies.add(clazz);
            }
        }
    }
}
TOP

Related Classes of org.apache.sling.jcr.jackrabbit.base.config.OsgiBeanFactory$DepFinderBeanConfigVisitor

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.