Package org.apache.sling.jcr.registration

Source Code of org.apache.sling.jcr.registration.AbstractRegistrationSupport

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

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.jcr.Repository;

import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.log.LogService;

/**
* The <code>AbstractRegistrationSupport</code> class is the base class for
* registration purposes of embedded repositories.
* <p>
* This base class cares for synchronization issues of the
* {@link #activate(ComponentContext)}, {@link #deactivate(ComponentContext)},
* {@link #bindRepository(ServiceReference)} and
* {@link #unbindRepository(ServiceReference)} methods. Implementations of the
* abstract API may safely assume to run thread-safe.
* <p>
* To ensure this thread-safeness, said methods should not be overwritten.
*/
public abstract class AbstractRegistrationSupport {

    /**
     * The JCR Repository service registration property used to create
     * the registration name. If this service registration property
     * (assumed to be a single string) does not exist, the repository is
     * not registered.
     */
    public static final String REPOSITORY_REGISTRATION_NAME = "name";

    /**
     * The LogService for logging. Extensions of this class must declare the log
     * service as a reference or call the {@link #bindLog(LogService)} to enable
     * logging correctly.
     */
    private volatile LogService log;

    /**
     * The OSGi ComponentContext.
     */
    private ComponentContext componentContext;

    /**
     * The (possibly empty) map of repositories which have been bound to the
     * registry before the registry has been activated.
     */
    private final Map<String, ServiceReference> repositoryRegistrationBacklog = new HashMap<String, ServiceReference>();

    /**
     * The map of repositories which have been bound to the registry component
     * and which are actually registered with the registry.
     */
    private final Map<String, Object> registeredRepositories = new HashMap<String, Object>();

    /**
     * A lock to serialize access to the registry management in this class.
     */
    protected final Object registryLock = new Object();

    // ---------- API to be implemented by extensions --------------------------

    /**
     * Performs additional activation tasks. This method is called by the
     * {@link #activate(ComponentContext)} method and is intended for internal
     * setup, such as acquiring the registry.
     *
     * @return Whether the activation succeeded or not. If <code>true</code>
     *         is returned, activation succeeded and any repositories which have
     *         been bound before the component was activated are now actually
     *         registered. If <code>false</code> is returned, activation
     *         failed and this component is disabled and receives no further
     *         repository bind and unbound events (apart for unbind events for
     *         repositories which have already been bound).
     */
    protected abstract boolean doActivate();

    /**
     * Performs additional deactivation tasks. This method is called by the
     * {@link #deactivate(ComponentContext)} method and is intended for internal
     * cleanup of setup done by the {@link #doActivate()} method.
     * <p>
     * This method is always called, regardless of whether {@link #doActivate()}
     * succeeded or not.
     */
    protected abstract void doDeactivate();

    /**
     * Called to actually register a repository with the registry. This method
     * is called by {@link #activate(ComponentContext)} for any repositories
     * bound before the component was activated and by
     * {@link #bindRepository(ServiceReference)} for any repositories bound
     * after the component was activated.
     * <p>
     * If actual registration fails, this method is expected to return
     * <code>null</code> to indicate this fact. In this case, the
     * {@link #unbindRepository(String, Object)} will NOT be called for the
     * named repository.
     * <p>
     * This method may safely assume that it is only called on or after
     * activation of this component on or before the component deactivation.
     *
     * @param name The name under which the repository is to be registered.
     * @param repository The <code>javax.jcr.Repository</code> to register.
     * @return Returns an object which is later given as the <code>data</code>
     *         parameter to the {@link #unbindRepository(String, Object)} method
     *         to unregister the repository of the given name. This may be
     *         <code>null</code> if actual registration failed.
     */
    protected abstract Object bindRepository(String name, Repository repository);

    /**
     * Called to actually unregister a repository with the registry. This method
     * is called by {@link #unbindRepository(ServiceReference)} for any
     * repositories unbound before the component is deactivated and by
     * {@link #deactivate(ComponentContext)} for any repositories not unbound
     * before the component is deactivated.
     * <p>
     * If the {@link #bindRepository(String, Repository)} returned
     * <code>null</code> for when the named repository was registered, this
     * method is not called.
     * <p>
     * This method may safely assume that it is only called on or after
     * activation of this component on or before the component deactivation.
     *
     * @param name The name under which the repository is to be registered.
     * @param data The data object returned by the
     *            {@link #bindRepositoryInternal(String, ServiceReference)}
     *            method.
     */
    protected abstract void unbindRepository(String name, Object data);

    // ---------- Implementation support methods -------------------------------

    /**
     * Returns the OSGi <code>ComponentContext</code> of this component. This
     * method returns <code>null</code> before the {@link #doActivate()}
     * method is called and after the {@link #doDeactivate()} method has been
     * called. That is, this method does not return <code>null</code> if it is
     * fully operational.
     */
    protected ComponentContext getComponentContext() {
        return this.componentContext;
    }

    /**
     * Logs a message with optional <code>Throwable</code> stack trace to the
     * log service or <code>stderr</code> if no log service is available.
     *
     * @param level The <code>LogService</code> level at which to log the
     *            message.
     * @param message The message to log, this should of course not be
     *            <code>null</code>.
     * @param t The <code>Throwable</code> to log along with the message. This
     *            may be <code>null</code>.
     */
    protected void log(int level, String message, Throwable t) {
        LogService log = this.log;
        if (log != null) {
            log.log(level, message, t);
        } else {
            System.err.print(level + " - " + message);
            if (t != null) {
                t.printStackTrace(System.err);
            }
        }

    }

    /**
     * Returns the <code>name</code> property from the service properties or
     * <code>null</code> if no such property exists or the property is an
     * empty string.
     *
     * @param reference The <code>ServiceReference</code> whose
     *            <code>name</code> property is to be returned.
     * @return The non-empty name property or <code>null</code>.
     */
    protected String getName(ServiceReference reference) {
        String name = (String) reference.getProperty(REPOSITORY_REGISTRATION_NAME);
        if (name == null || name.length() == 0) {
            log(LogService.LOG_DEBUG,
                    "registerRepository: Repository not to be registered", null);
            return null;
        }

        return name;
    }

    // ---------- SCR intergration ---------------------------------------------

    /**
     * Activates this component thread-safely as follows:
     * <ol>
     * <li>Set the OSGi ComponentContext field
     * <li>Call {@link #doActivate()}
     * <li>Register repositores bound before activation calling
     * {@link #bindRepository(String, Repository)} for each such repository.
     * </ol>
     * <p>
     * If {@link #doActivate()} returns <code>false</code>, the repositories
     * already bound are not actually registered, but this component is
     * disabled.
     *
     * @param componentContext The OSGi <code>ComponentContext</code> of this
     *      component.
     */
    protected void activate(ComponentContext componentContext) {
        synchronized (this.registryLock) {
            this.componentContext = componentContext;

            if (this.doActivate()) {
                // register all repositories in the tmp map
                for (Iterator<Map.Entry<String, ServiceReference>> ri = this.repositoryRegistrationBacklog.entrySet().iterator(); ri.hasNext();) {
                    Map.Entry<String, ServiceReference> entry = ri.next();

                    this.bindRepositoryInternal(entry.getKey(),
                        entry.getValue());

                    ri.remove();
                }
            } else {
                // disable this component
                String name = (String) componentContext.getProperties().get(
                    ComponentConstants.COMPONENT_NAME);
                this.getComponentContext().disableComponent(name);
            }
        }
    }

    /**
     * Deactivates this component thread-safely as follows:
     * <ol>
     * <li>Unregister repositores still bound calling
     * {@link #unbindRepository(String, Object)} for each such repository.
     * <li>Call {@link #doDeactivate()}
     * <li>Clear the OSGi ComponentContext field
     * </ol>
     *
     * @param componentContext The OSGi <code>ComponentContext</code> of this
     *            component.
     */
    protected void deactivate(ComponentContext context) {

        synchronized (this.registryLock) {

            // unregister all repositories in the tmp map
            for (Iterator<Map.Entry<String, Object>> ri = this.registeredRepositories.entrySet().iterator(); ri.hasNext();) {
                Map.Entry<String, Object> entry = ri.next();

                this.unbindRepository(entry.getKey(), entry.getValue());

                ri.remove();
            }

            this.doDeactivate();

            this.componentContext = null;
        }
    }

    /**
     * Registers the repository identified by the OSGi service reference under
     * the name set as service property. If the repository service has not
     * name property, the repository is not registered.
     *
     * @param reference The <code>ServiceReference</code> representing the
     *      repository to register.
     */
    protected void bindRepository(ServiceReference reference) {
        String name = this.getName(reference);

        if (name != null) {
            synchronized (this.registryLock) {
                if (this.componentContext == null) {
                    // no context to register with, delay ??
                    this.repositoryRegistrationBacklog.put(name, reference);
                } else {
                    this.bindRepositoryInternal(name, reference);
                }
            }
        } else {
            this.log(LogService.LOG_INFO, "Service "
                + reference.getProperty(Constants.SERVICE_ID)
                + " has no name property, not registering", null);
        }
    }

    /**
     * Unregisters the repository identified by the OSGi service reference under
     * the name set as service property. If the repository service has no
     * name property, the repository is assumed not be registered and nothing
     * needs to be done.
     *
     * @param reference The <code>ServiceReference</code> representing the
     *      repository to unregister.
     */
    protected void unbindRepository(ServiceReference reference) {
        String name = this.getName(reference);
        if (name != null) {

            synchronized (this.registryLock) {
                // unbind the repository
                Object data = this.registeredRepositories.remove(name);
                if (data != null) {
                    this.unbindRepository(name, data);
                }

                // ensure unregistered from internal and temporary map
                this.repositoryRegistrationBacklog.remove(name);

                // make sure we have no reference to the service
                if (this.componentContext != null) {
                    this.componentContext.getBundleContext().ungetService(reference);
                }
            }
        } else {
            this.log(LogService.LOG_DEBUG, "Service "
                + reference.getProperty(Constants.SERVICE_ID)
                + " has no name property, nothing to unregister", null);
        }
    }

    /** Binds the LogService */
    protected void bindLog(LogService log) {
        this.log = log;
    }

    /** Unbinds the LogService */
    protected void unbindLog(LogService log) {
        if ( this.log == log ) {
            this.log = null;
        }
    }

    //---------- internal -----------------------------------------------------

    /**
     * Internal bind method called by {@link #activate(ComponentContext)} and
     * {@link #bindRepository(ServiceReference)} to actually control the
     * registration process by retrieving the repository and calling the
     * {@link #bindRepository(String, Repository)} method.
     */
    private void bindRepositoryInternal(String name, ServiceReference reference) {
        Repository repository = (Repository) this.getComponentContext().getBundleContext().getService(
            reference);
        Object data = this.bindRepository(name, repository);
        if (data != null) {
            this.registeredRepositories.put(name, data);
        }
    }

}
TOP

Related Classes of org.apache.sling.jcr.registration.AbstractRegistrationSupport

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.