Package org.apache.sling.jcr.ocm.impl

Source Code of org.apache.sling.jcr.ocm.impl.ObjectContentManagerFactoryImpl

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

import static org.apache.sling.jcr.ocm.OcmConstants.EVENT_MAPPING_ADDED;
import static org.apache.sling.jcr.ocm.OcmConstants.EVENT_MAPPING_REMOVED;
import static org.apache.sling.jcr.ocm.OcmConstants.MAPPER_BUNDLE_HEADER;
import static org.apache.sling.jcr.ocm.OcmConstants.MAPPING_CLASS;
import static org.apache.sling.jcr.ocm.OcmConstants.MAPPING_NODE_TYPE;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFactory;

import org.apache.felix.scr.annotations.Component;
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.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.ocm.manager.ObjectContentManager;
import org.apache.jackrabbit.ocm.manager.atomictypeconverter.AtomicTypeConverterProvider;
import org.apache.jackrabbit.ocm.manager.cache.ObjectCache;
import org.apache.jackrabbit.ocm.manager.impl.ObjectContentManagerImpl;
import org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter;
import org.apache.jackrabbit.ocm.manager.objectconverter.impl.ObjectConverterImpl;
import org.apache.jackrabbit.ocm.manager.objectconverter.impl.ProxyManagerImpl;
import org.apache.jackrabbit.ocm.mapper.model.MappingDescriptor;
import org.apache.jackrabbit.ocm.query.QueryManager;
import org.apache.jackrabbit.ocm.query.impl.QueryManagerImpl;
import org.apache.jackrabbit.ocm.reflection.ReflectionUtils;
import org.apache.jackrabbit.value.ValueFactoryImpl;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.apache.sling.jcr.ocm.ObjectContentManagerFactory;
import org.apache.sling.jcr.ocm.impl.classloader.MapperClassLoader;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.v1.XmlPullParserException;

/**
* The <code>ObjectContentManagerFactory</code> TODO
*
*/
@Component
@Service(value=ObjectContentManagerFactory.class)
@Properties({
    @Property(name="service.description", value="Sling Object Content Manager Factory"),
    @Property(name="service.vendor", value="The Apache Software Foundation")
})
public class ObjectContentManagerFactoryImpl implements ObjectContentManagerFactory, SynchronousBundleListener {

    /** default log */
    private static final Logger log = LoggerFactory.getLogger(ObjectContentManagerFactoryImpl.class);

    @Reference(cardinality=ReferenceCardinality.OPTIONAL_UNARY, policy=ReferencePolicy.DYNAMIC)
    private volatile EventAdmin eventAdmin;

    /**
     * The class loader used by the Jackrabbit OCM ReflectionUtils class to load
     * classes for mapping. The class loader is set on the ReflectionUtils
     * before the new mappings are loaded.
     *
     * @see #loadMappings()
     */
    private MapperClassLoader mapperClassLoader;

    private BundleMapper mapper;

    private ClassDescriptorReader descriptorReader;

    private AtomicTypeConverterProvider converterProvider;

    private OcmAdapterFactory adapterFactory;

    private OcmFactoryAdapterFactory factoryAdapterFactory;

    public ObjectContentManagerFactoryImpl() {

        // prepare the data converters and query manager
        this.converterProvider = new SlingAtomicTypeConverterProvider();
    }

    /**
     * @see org.apache.sling.jcr.ocm.ObjectContentManagerFactory#getObjectContentManager(javax.jcr.Session)
     */
    public ObjectContentManager getObjectContentManager(Session session) {

        ValueFactory valueFactory;
        try {
            valueFactory = session.getValueFactory();
        } catch (RepositoryException re) {
            log.info(
                "getObjectContentManager: Cannot get ValueFactory from Session ("
                    + session.getUserID() + "), using default factory", re);
            valueFactory = ValueFactoryImpl.getInstance();
        }

        ObjectCache objectCache = new ObservingObjectCache(session);

        QueryManager queryManager = new QueryManagerImpl(mapper,
            converterProvider.getAtomicTypeConverters(), valueFactory);

        ObjectConverter objectConverter = new ObjectConverterImpl(mapper,
            converterProvider, new ProxyManagerImpl(), objectCache);

        return new ObjectContentManagerImpl(mapper, objectConverter,
            queryManager, objectCache, session);
    }

    private ClassDescriptorReader getDescriptorReader() {
        if (descriptorReader == null) {
            ClassDescriptorReader ddr = new ClassDescriptorReader();
            // ddr.setResolver(null /* TODO resolve URL :
            // graffito-jcr-mapping.dtd */);
            // ddr.setValidating(false);
            descriptorReader = ddr;
        } else {
            descriptorReader.reset();
        }

        return descriptorReader;
    }

    // TODO: New Implementation --------------------------------------

    private List<Bundle> bundles = new ArrayList<Bundle>();

    /**
     * Loads the components of the given bundle. If the bundle has no
     * <i>Service-Component</i> header, this method has no effect. The
     * fragments of a bundle are not checked for the header (112.4.1).
     * <p>
     * This method calls the {@link #getBundleContext(Bundle)} method to find
     * the <code>BundleContext</code> of the bundle. If the context cannot be
     * found, this method does not load components for the bundle.
     */
    private synchronized void addBundle(Bundle bundle) {
        // ignore bundle without mappings
        if (bundle.getHeaders().get(MAPPER_BUNDLE_HEADER) == null) {
            return;
        }

        if (bundles.contains(bundle)) {
            // ignore existing bundle
            return;
        }

        bundles.add(bundle);

        loadMappings();

        // fire mapping event
        fireEvent(bundle, EVENT_MAPPING_ADDED);
    }

    private synchronized void removeBundle(Bundle bundle) {
        if (!bundles.remove(bundle)) {
            // bundle not known
            return;
        }

        loadMappings();

        // fire mapping event
        fireEvent(bundle, EVENT_MAPPING_REMOVED);
    }

    private void loadMappings() {
        MapperClassLoader newMapperClassLoader = new MapperClassLoader();
        ReflectionUtils.setClassLoader(newMapperClassLoader);

        ArrayList<URL> urlList = new ArrayList<URL>();
        for (Iterator<Bundle> bi = bundles.iterator(); bi.hasNext();) {
            Bundle bundle = bi.next();

            String mapperHeader = (String) bundle.getHeaders().get(
                MAPPER_BUNDLE_HEADER);
            if (mapperHeader == null) {
                // no components in the bundle, abandon
                log.debug("registerMapperClient: Bundle {} has no mappings",
                    bundle.getSymbolicName());
                continue;
            }

            newMapperClassLoader.registerBundle(bundle);

            StringTokenizer tokener = new StringTokenizer(mapperHeader, ",");
            while (tokener.hasMoreTokens()) {
                String mapping = tokener.nextToken().trim();
                URL mappingURL = bundle.getResource(mapping);
                if (mappingURL == null) {
                    log.warn("Mapping {} not found in bundle {}", mapping,
                        bundle.getSymbolicName());
                } else {
                    urlList.add(mappingURL);
                }
            }
        }

        // nothing to do if there are not streams
        if (urlList.isEmpty()) {
            return;
        }

        MappingDescriptor md;
        try {
            ClassDescriptorReader cdr = getDescriptorReader();
            cdr.parse(urlList);
            md = cdr.getMappingDescriptor();
        } catch (XmlPullParserException xppe) {
            log.error("Failed parsing descriptors", xppe);
            return;
        } catch (IOException ioe) {
            log.error("Failed reading descriptors", ioe);
            return;
        }

        BundleMapper newMapper = new BundleMapper(md);

        // dispose off old class loader before using new loader
        if (mapperClassLoader != null) {
            // note: the mapperClassLoader is used by the Jackrabbit OCM
            // ReflectionUtils class. This class has already been reset to
            // use the new class loader, so we can dispose off the old
            // class loader here safely
            mapperClassLoader.dispose();
        }

        mapperClassLoader = newMapperClassLoader;
        mapper = newMapper;

        // update adapters
        if (adapterFactory != null) {
            adapterFactory.updateAdapterClasses(mapper.getMappedClasses());
        }
    }

    // ---------- BundleListener -----------------------------------------------

    /**
     * Loads and unloads any components provided by the bundle whose state
     * changed. If the bundle has been started, the components are loaded. If
     * the bundle is about to stop, the components are unloaded.
     *
     * @param event The <code>BundleEvent</code> representing the bundle state
     *            change.
     */
    public void bundleChanged(BundleEvent event) {

        //
        // NOTE:
        // This is synchronous - take care to not block the system !!
        //

        switch (event.getType()) {

            case BundleEvent.STARTING: // STARTED:
                // register mappings before the bundle gets activated
                addBundle(event.getBundle());
                break;

            case BundleEvent.STOPPED:
                // remove mappings after the bundle has stopped
                removeBundle(event.getBundle());
                break;

        }
    }

    // ---------- EventAdmin Event Dispatching ---------------------------------

    /**
     * Fires an OSGi event through the EventAdmin service.
     *
     * @param sourceBundle The Bundle from which the event originates. This may
     *            be <code>null</code> if there is no originating bundle.
     * @param eventName The name of the event
     * @throws NullPointerException if eventName or props is <code>null</code>.
     */
    public void fireEvent(Bundle sourceBundle, String eventName) {

        // check event admin service, return if not available
        EventAdmin ea = eventAdmin;
        BundleMapper mapper = this.mapper;
        if (ea == null || mapper == null) {
            return;
        }

        // only fire, if there is a (new) mapper
        Map<String, Object> props = new HashMap<String, Object>();
        props.put(MAPPING_CLASS, mapper.getMappedClasses());
        props.put(MAPPING_NODE_TYPE, mapper.getMappedNodeTypes());

        // create and fire the event
        Event event = OsgiUtil.createEvent(sourceBundle, null, eventName, props);
        ea.postEvent(event);
    }

    // ---------- SCR Integration ---------------------------------------------

    /** Activates this component, called by SCR before registering as a service */
    protected void activate(ComponentContext componentContext) {

        componentContext.getBundleContext().addBundleListener(this);

        try {
            Bundle[] bundles = componentContext.getBundleContext().getBundles();
            for (Bundle bundle : bundles) {
                if (bundle.getState() == Bundle.ACTIVE) {
                    // register active bundles with the mapper client
                    addBundle(bundle);
                }
            }
        } catch (Throwable t) {
            log.error("activate: Problem while loading initial content and"
                + " registering mappings for existing bundles", t);
        }

        // register the OcmAdapterFactory
        String[] mappedClasses = (mapper != null)
                ? mapper.getMappedClasses()
                : new String[0];
        adapterFactory = new OcmAdapterFactory(this,
            componentContext.getBundleContext(), mappedClasses);
        factoryAdapterFactory = new OcmFactoryAdapterFactory(this, componentContext.getBundleContext());
    }

    /** Deativates this component, called by SCR to take out of service */
    protected void deactivate(ComponentContext componentContext) {
        if (adapterFactory != null) {
            adapterFactory.dispose();
            adapterFactory = null;
        }
        if ( factoryAdapterFactory != null ) {
            factoryAdapterFactory.dispose();
            factoryAdapterFactory = null;
        }

        componentContext.getBundleContext().removeBundleListener(this);

        synchronized (this) {
            if (mapperClassLoader != null) {
                mapperClassLoader.dispose();
                mapperClassLoader = null;
            }
        }
    }

}
TOP

Related Classes of org.apache.sling.jcr.ocm.impl.ObjectContentManagerFactoryImpl

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.