All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.sling.jcr.base.AbstractSlingRepositoryManager Maven / Gradle / Ivy

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

import java.util.Dictionary;

import javax.jcr.Repository;

import org.apache.sling.jcr.api.SlingRepository;
import org.apache.sling.serviceusermapping.ServiceUserMapper;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import aQute.bnd.annotation.ProviderType;

/**
 * The AbstractSlingRepositoryManager is the basis for controlling
 * the JCR repository instances used by Sling. As a manager it starts and stops
 * the actual repository instance, manages service registration and hands out
 * {@code SlingRepository} instances to be used by the consumers.
 * 

* This base class controls the livecycle of repository instance whereas * implementations of this class provide actual integration into the runtime * context. The livecycle of the repository instance is defined as follows: *

* To start the repository instance, the implemetation calls the * {@link #start(BundleContext, String, boolean)}method which goes through the * steps of instantiating the repository, setting things up, and registering the * repository as an OSGi service: *

    *
  1. {@link #acquireRepository()}
  2. *
  3. {@link #create(Bundle)}
  4. *
  5. {@link #setup(BundleContext, SlingRepository)}
  6. *
  7. {@link #registerService()}
  8. *
*

* To stop the repository instance, the implementation calls the {@link #stop()} * method which goes through the setps of unregistering the OSGi service, * tearing all special settings down and finally shutting down the repository: *

    *
  1. {@link #unregisterService(ServiceRegistration)}
  2. *
  3. {@link #tearDown()}
  4. *
  5. {@link #destroy(AbstractSlingRepository2)}
  6. *
  7. {@link #disposeRepository(Repository)}
  8. *
*

* Instances of this class manage a single repository instance backing the OSGi * service instances. Each consuming bundle, though, gets its own service * instance backed by the single actual repository instance managed by this * class. * * @see AbstractSlingRepository2 * @since API version 2.3 (bundle version 2.2.2) */ @ProviderType public abstract class AbstractSlingRepositoryManager extends NamespaceMappingSupport { /** default log */ private final Logger log = LoggerFactory.getLogger(getClass()); private volatile BundleContext bundleContext; private volatile Repository repository; // the SlingRepository instance used to setup basic stuff // see setup and tearDown private volatile AbstractSlingRepository2 masterSlingRepository; private volatile ServiceRegistration repositoryService; private volatile String defaultWorkspace; private volatile boolean disableLoginAdministrative; // private volatile ServiceTracker repoInitializerTracker; /** * Returns the default workspace, which may be null meaning to * use the repository provided default workspace. * * @return the default workspace or {@code null} indicating the repository's * default workspace is actually used. */ public final String getDefaultWorkspace() { return defaultWorkspace; } /** * Returns whether to disable the * {@code SlingRepository.loginAdministrative} method or not. * * @return {@code true} if {@code SlingRepository.loginAdministrative} is * disabled. */ public final boolean isDisableLoginAdministrative() { return disableLoginAdministrative; } /** * Returns the {@code ServiceUserMapper} service to map the service name to * a service user name. *

* The {@code ServiceUserMapper} is used to implement the * {@link AbstractSlingRepository2#loginService(String, String)} method used * to replace the * {@link AbstractSlingRepository2#loginAdministrative(String)} method. If * this method returns {@code null} and hence the * {@code ServiceUserMapperService} is not available, the * {@code loginService} method is not able to login. * * @return The {@code ServiceUserMapper} service or {@code null} if not * available. * @see AbstractSlingRepository2#loginService(String, String) */ protected abstract ServiceUserMapper getServiceUserMapper(); /** * Creates the backing JCR repository instances. It is expected for this * method to just start the repository. *

* This method does not throw any Throwable but instead just * returns null if not repository is available. Any problems * trying to acquire the repository must be caught and logged as * appropriate. * * @return The acquired JCR Repository or null if * not repository can be acquired. * @see #start(BundleContext, String, boolean) */ protected abstract Repository acquireRepository(); /** * Registers this component as an OSGi service with the types provided by * the {@link #getServiceRegistrationInterfaces()} method and properties * provided by the {@link #getServiceRegistrationProperties()} method. *

* The repository is actually registered as an OSGi {@code ServiceFactory} * where the {@link #create(Bundle)} method is called to create an actual * {@link AbstractSlingRepository2} repository instance for a calling * (using) bundle. When the bundle is done using the repository instance, * the {@link #destroy(AbstractSlingRepository2)} method is called to clean * up. * * @return The OSGi ServiceRegistration object representing the * registered service. * @see #start(BundleContext, String, boolean) * @see #getServiceRegistrationInterfaces() * @see #getServiceRegistrationProperties() * @see #create(Bundle) * @see #destroy(AbstractSlingRepository2) */ protected final ServiceRegistration registerService() { final Dictionary props = getServiceRegistrationProperties(); final String[] interfaces = getServiceRegistrationInterfaces(); return bundleContext.registerService(interfaces, new ServiceFactory() { @Override public Object getService(Bundle bundle, ServiceRegistration registration) { return AbstractSlingRepositoryManager.this.create(bundle); } @Override public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) { AbstractSlingRepositoryManager.this.destroy((AbstractSlingRepository2) service); } }, props); } /** * Return the service registration properties to be used to register the * repository service in {@link #registerService()}. * * @return The service registration properties to be used to register the * repository service in {@link #registerService()} * @see #registerService() */ protected abstract Dictionary getServiceRegistrationProperties(); /** * Returns the service types to be used to register the repository service * in {@link #registerService()}. All interfaces returned must be accessible * to the class loader of the class of this instance. *

* This method may be overwritten to return additional types but the types * returned from this base implementation, {@code SlingRepository} and * {@code Repository}, must always be included. * * @return The service types to be used to register the repository service * in {@link #registerService()} * @see #registerService() */ protected String[] getServiceRegistrationInterfaces() { return new String[] { SlingRepository.class.getName(), Repository.class.getName() }; } /** * Creates an instance of the {@link AbstractSlingRepository2} * implementation for use by the given {@code usingBundle}. *

* This method is called when the repository service is requested from * within the using bundle for the first time. *

* This method is expected to return a new instance on every call. * * @param usingBundle The bundle providing from which the repository is * requested. * @return The {@link AbstractSlingRepository2} implementation instance to * be used by the {@code usingBundle}. * @see #registerService() */ protected abstract AbstractSlingRepository2 create(Bundle usingBundle); /** * Cleans up the given {@link AbstractSlingRepository2} instance previously * created by the {@link #create(Bundle)} method. * * @param repositoryServiceInstance The {@link AbstractSlingRepository2} * istance to cleanup. * @see #registerService() */ protected abstract void destroy(AbstractSlingRepository2 repositoryServiceInstance); /** * Returns the repository underlying this instance or null if * no repository is currently being available. * * @return The repository */ protected final Repository getRepository() { return repository; } /** * Unregisters the service represented by the * serviceRegistration. * * @param serviceRegistration The service to unregister */ protected final void unregisterService(ServiceRegistration serviceRegistration) { serviceRegistration.unregister(); } /** * Disposes off the given repository. * * @param repository The repository to be disposed off which is the same as * the one returned from {@link #acquireRepository()}. */ protected abstract void disposeRepository(Repository repository); // --------- SCR integration ----------------------------------------------- /** * This method actually starts the backing repository instannce and * registeres the repository service. *

* Multiple subsequent calls to this method without calling {@link #stop()} * first have no effect. * * @param bundleContext The {@code BundleContext} to register the repository * service (and optionally more services required to operate the * repository) * @param defaultWorkspace The name of the default workspace to use to * login. This may be {@code null} to have the actual repository * instance define its own default * @param disableLoginAdministrative Whether to disable the * {@code SlingRepository.loginAdministrative} method or not. * @return {@code true} if the repository has been started and the service * is registered. */ protected final boolean start(final BundleContext bundleContext, final String defaultWorkspace, final boolean disableLoginAdministrative) { // already setup ? if (this.bundleContext != null) { log.debug("start: Repository already started and registered"); return true; } this.bundleContext = bundleContext; this.defaultWorkspace = defaultWorkspace; this.disableLoginAdministrative = disableLoginAdministrative; // this.repoInitializerTracker = new ServiceTracker(bundleContext, SlingRepositoryInitializer.class.getName(), null); // this.repoInitializerTracker.open(); try { log.debug("start: calling acquireRepository()"); Repository newRepo = this.acquireRepository(); if (newRepo != null) { // ensure we really have the repository log.debug("start: got a Repository"); this.repository = newRepo; this.masterSlingRepository = this.create(this.bundleContext.getBundle()); log.debug("start: setting up NamespaceMapping support"); this.setup(this.bundleContext, this.masterSlingRepository); /* log.debug("start: calling SlingRepositoryInitializer"); Throwable t = null; try { executeRepositoryInitializers(this.masterSlingRepository); } catch(Exception e) { t = e; } catch(Error e) { t = e; } if(t != null) { log.error("Exception in a SlingRepositoryInitializer, SlingRepository service registration aborted", t); return false; } */ log.debug("start: calling registerService()"); this.repositoryService = registerService(); log.debug("start: registerService() successful, registration=" + repositoryService); return true; } } catch (Throwable t) { // consider an uncaught problem an error log.error("start: Uncaught Throwable trying to access Repository, calling stopRepository()", t); // repository might be partially started, stop anything left stop(); } // fallback to failure to start the repository return false; } /* private void executeRepositoryInitializers(SlingRepository repo) throws Exception { final ServiceReference [] refs = repoInitializerTracker.getServiceReferences(); if(refs == null || refs.length == 0) { log.debug("No SlingRepositoryInitializer services found"); return; } Arrays.sort(refs); for(ServiceReference ref : refs) { final SlingRepositoryInitializer sri = (SlingRepositoryInitializer)bundleContext.getService(ref); log.debug("Executing {}", sri); try { sri.processRepository(repo); } finally { bundleContext.ungetService(ref); } } } */ /** * This method must be called if overwritten by implementations !! */ protected final void stop() { // if(repoInitializerTracker != null) { // repoInitializerTracker.close(); // repoInitializerTracker = null; // } // ensure the repository is really disposed off if (repository != null || repositoryService != null) { log.info("stop: Repository still running, forcing shutdown"); try { if (repositoryService != null) { try { log.debug("stop: Unregistering SlingRepository service, registration=" + repositoryService); unregisterService(repositoryService); } catch (Throwable t) { log.info("stop: Uncaught problem unregistering the repository service", t); } repositoryService = null; } if (repository != null) { Repository oldRepo = repository; repository = null; // stop namespace support this.tearDown(); this.destroy(this.masterSlingRepository); try { disposeRepository(oldRepo); } catch (Throwable t) { log.info("stop: Uncaught problem disposing the repository", t); } } } catch (Throwable t) { log.warn("stop: Unexpected problem stopping repository", t); } } this.repositoryService = null; this.repository = null; this.defaultWorkspace = null; this.bundleContext = null; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy