| /* |
| * 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.apache.felix.scr.annotations.Component; |
| 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.References; |
| 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. |
| */ |
| @Component(componentAbstract = true) |
| @References({ |
| @Reference( |
| name = "Repository", |
| policy = ReferencePolicy.DYNAMIC, |
| cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, |
| referenceInterface = Repository.class) |
| }) |
| 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. |
| */ |
| @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.DYNAMIC) |
| private 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) { |
| this.log.log(LogService.LOG_DEBUG, |
| "registerRepository: Repository not to be registered"); |
| 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) { |
| 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); |
| } |
| } |
| |
| } |