Return-Path: Delivered-To: apmail-incubator-sling-commits-archive@locus.apache.org Received: (qmail 48619 invoked from network); 5 Mar 2008 11:35:40 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 5 Mar 2008 11:35:40 -0000 Received: (qmail 27991 invoked by uid 500); 5 Mar 2008 11:35:36 -0000 Delivered-To: apmail-incubator-sling-commits-archive@incubator.apache.org Received: (qmail 27974 invoked by uid 500); 5 Mar 2008 11:35:36 -0000 Mailing-List: contact sling-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: sling-dev@incubator.apache.org Delivered-To: mailing list sling-commits@incubator.apache.org Received: (qmail 27965 invoked by uid 99); 5 Mar 2008 11:35:36 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 05 Mar 2008 03:35:36 -0800 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 05 Mar 2008 11:35:08 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 9D8F71A9832; Wed, 5 Mar 2008 03:35:18 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r633823 - in /incubator/sling/trunk/jcr/base: ./ src/main/java/org/apache/sling/jcr/base/ src/main/java/org/apache/sling/jcr/base/internal/ src/main/java/org/apache/sling/jcr/base/internal/loader/ Date: Wed, 05 Mar 2008 11:35:13 -0000 To: sling-commits@incubator.apache.org From: fmeschbe@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080305113518.9D8F71A9832@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: fmeschbe Date: Wed Mar 5 03:35:06 2008 New Revision: 633823 URL: http://svn.apache.org/viewvc?rev=633823&view=rev Log: SLING-132 Refactor repository access based on a background thread checking for the repository constantly and providing proper API to acquire, setup, teardown and dispose repositories. Modified: incubator/sling/trunk/jcr/base/pom.xml incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository.java incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/internal/SessionPool.java incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/internal/loader/Loader.java Modified: incubator/sling/trunk/jcr/base/pom.xml URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/base/pom.xml?rev=633823&r1=633822&r2=633823&view=diff ============================================================================== --- incubator/sling/trunk/jcr/base/pom.xml (original) +++ incubator/sling/trunk/jcr/base/pom.xml Wed Mar 5 03:35:06 2008 @@ -54,6 +54,8 @@ org.apache.felix maven-scr-plugin + + 1.0.4-SNAPSHOT org.apache.felix @@ -95,6 +97,8 @@ org.apache.jackrabbit jackrabbit-jcr-rmi + + 1.5-SNAPSHOT compile Modified: incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository.java?rev=633823&r1=633822&r2=633823&view=diff ============================================================================== --- incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository.java (original) +++ incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository.java Wed Mar 5 03:35:06 2008 @@ -35,79 +35,109 @@ import org.apache.sling.jcr.base.internal.SessionPoolFactory; import org.apache.sling.jcr.base.internal.SessionPoolManager; import org.apache.sling.jcr.base.internal.loader.Loader; -import org.osgi.framework.Bundle; +import org.apache.sling.jcr.base.util.RepositoryAccessor; +import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; +import org.osgi.framework.ServiceRegistration; import org.osgi.framework.SynchronousBundleListener; import org.osgi.service.component.ComponentContext; import org.osgi.service.log.LogService; - /** - * The AbstractSlingRepository is an abstract implementation of the - * {@link SlingRepository} interface which provides core support for session + * The AbstractSlingRepository is an abstract implementation of + * the {@link SlingRepository} interface which provides core support for session * pooling. Implementations of the SlingRepository interface may * wish to extend this class to benefit from a default implementation. *

* Extensions of this class will have to declare the following * scr.property tags to have them declared automatically in the * respective component and metatype definitions by the maven-sling-plugin: - * - *

- *  scr.property value="" name="defaultWorkspace"
- *  scr.property value="anonymous" name="anonymous.name"
- *  scr.property value="anonymous" name="anonymous.password"
- *  scr.property value="admin" name="admin.name"
- *  scr.property value="admin" name="admin.password"
- *  scr.property value="10" type="Integer" name="pool.maxActive"
- *  scr.property value="10" type="Integer" name="pool.maxIdle"
- *  scr.property value="256" type="Integer" name="pool.maxActiveWait"
- * 
+ * * @scr.component */ -public abstract class AbstractSlingRepository - implements SlingRepository, SynchronousBundleListener { +public abstract class AbstractSlingRepository implements SlingRepository, + SynchronousBundleListener, Runnable { + /** @scr.property value="" */ public static final String PROPERTY_DEFAULT_WORKSPACE = "defaultWorkspace"; + /** @scr.property valueRef="DEFAULT_ANONYMOUS_USER" */ public static final String PROPERTY_ANONYMOUS_USER = "anonymous.name"; + /** @scr.property valueRef="DEFAULT_ANONYMOUS_PASS" */ public static final String PROPERTY_ANONYMOUS_PASS = "anonymous.password"; + /** @scr.property valueRef="DEFAULT_ADMIN_USER" */ public static final String PROPERTY_ADMIN_USER = "admin.name"; + /** @scr.property valueRef="DEFAULT_ADMIN_PASS" */ public static final String PROPERTY_ADMIN_PASS = "admin.password"; - - public static final String DEFAULT_WORKSPACE = "default"; - - public static final String DEFAULT_ANONYMOUS_USER = "anonymous"; - - public static final String DEFAULT_ANONYMOUS_PASS = "anonymous"; - - public static final String DEFAULT_ADMIN_USER = "admin"; - - public static final String DEFAULT_ADMIN_PASS = "admin"; + + /** @scr.property valueRef="DEFAULT_POLL_ACTIVE" */ + public static final String PROPERTY_POLL_ACTIVE = "poll.active"; + + /** @scr.property valueRef="DEFAULT_POLL_INACTIVE" */ + public static final String PROPERTY_POLL_INACTIVE = "poll.inactive"; /** * The name of the configuration parameter containing the maximum number of * seconds to wait for the number of currently active sessions to drop be * low the upper limit before giving up (value is "pool.maxActiveWait"). + * + * @scr.property value="1" type="Integer" */ - public static final String PARAM_MAX_ACTIVE_SESSIONS_WAIT = "pool.maxActiveWait"; + public static final String PROPERTY_MAX_ACTIVE_SESSIONS_WAIT = "pool.maxActiveWait"; /** * The name of the configuration parameter containing the upper limit of the * simultaneously active sessions (value is "pool.maxActive"). + * + * @scr.property value="-1" type="Integer" */ - public static final String PARAM_MAX_ACTIVE_SESSIONS = "pool.maxActive"; + public static final String PROPERTY_MAX_ACTIVE_SESSIONS = "pool.maxActive"; /** * The name of the configuration parameter containing the upper limit of the * currently idle sessions to keep in the pool (value is "pool.maxIdle"). + * + * @scr.property value="10" type="Integer" */ - public static final String PARAM_MAX_IDLE_SESSIONS = "pool.maxIdle"; + public static final String PROPERTY_MAX_IDLE_SESSIONS = "pool.maxIdle"; + + public static final String DEFAULT_WORKSPACE = "default"; + + public static final String DEFAULT_ANONYMOUS_USER = "anonymous"; + public static final String DEFAULT_ANONYMOUS_PASS = "anonymous"; + + public static final String DEFAULT_ADMIN_USER = "admin"; + + public static final String DEFAULT_ADMIN_PASS = "admin"; + + /** + * The default value for the number of seconds to wait between two + * consecutive checks while the repository is active (value is 10). + */ + public static final int DEFAULT_POLL_ACTIVE = 10; + + /** + * The default value for the number of seconds to wait between two + * consecutive checks while the repository is not active (value is 10). + */ + public static final int DEFAULT_POLL_INACTIVE = 10; + + /** The minimum number of seconds allowed for any of the two poll times */ + public static final int MIN_POLL = 2; + + /** @scr.reference bind="bindLog" unbind="unbindLog" */ + private LogService log; + private ComponentContext componentContext; + private Repository repository; + + private ServiceRegistration repositoryService; + private String defaultWorkspace; private String anonUser; @@ -122,60 +152,28 @@ private Loader loader; - protected AbstractSlingRepository() { - } - - protected abstract Repository getDelegatee() throws RepositoryException; - - protected final SessionPoolManager getPoolManager() - throws RepositoryException { - if (this.poolManager == null) { - this.poolManager = new SessionPoolManager(this.getDelegatee(), this.loader, - this.getSessionPoolFactory()); - } + // the poll interval used while the repository is not active + private long pollTimeInActive; - return this.poolManager; - } + // the poll interval used while the repository is active + private long pollTimeActive; - /** - * @return - */ - protected SessionPoolFactory getSessionPoolFactory() { - @SuppressWarnings("unchecked") - Dictionary properties = this.componentContext.getProperties(); - final int maxActiveSessions = this.getIntProperty(properties, - PARAM_MAX_ACTIVE_SESSIONS); - final int maxIdleSessions = this.getIntProperty(properties, - PARAM_MAX_IDLE_SESSIONS); - final int maxActiveSessionsWait = this.getIntProperty(properties, - PARAM_MAX_ACTIVE_SESSIONS_WAIT); - return new SessionPoolFactory() { + // whether the repository checker task should be active. this field + // is managed by the startRepositoryPinger and stopRepositoryPinger methods + private boolean running; - public SessionPool createPool(final SessionPoolManager mgr, final SimpleCredentials credentials) { - // create and configure the new pool - final SessionPool pool = createSessionPool(mgr, credentials); - pool.setMaxActiveSessions(maxActiveSessions); - pool.setMaxActiveSessionsWait(maxActiveSessionsWait); - pool.setMaxIdleSessions(maxIdleSessions); - return pool; - } - }; - } + // the background thread constantly checking the repository + private Thread repositoryPinger; - protected SessionPool createSessionPool(final SessionPoolManager mgr, final SimpleCredentials credentials) { - final SessionPool pool = new SessionPool(mgr, credentials); - return pool; + protected AbstractSlingRepository() { } /** - * @see org.apache.sling.jcr.api.SlingRepository#getDefaultWorkspace() - * Declared final to make sure the SLING-256 rule is enforced. + * Returns the default workspace, which may be null meaning + * to use the repository provided default workspace. */ public final String getDefaultWorkspace() { - if(defaultWorkspace == null || defaultWorkspace.trim().length() == 0) { - // SLING-256 - return null; - } + // Declared final to make sure the SLING-256 rule is enforced. return this.defaultWorkspace; } @@ -187,49 +185,29 @@ return this.login(null, null); } - /* - * (non-Javadoc) - * - * @see org.apache.sling.core.jcr.SessionProvider#getAdministrationSession() - */ public Session loginAdministrative(String workspace) throws RepositoryException { - SimpleCredentials sc = new SimpleCredentials(this.adminUser, this.adminPass); + SimpleCredentials sc = new SimpleCredentials(this.adminUser, + this.adminPass); return this.login(sc, workspace); } - /* - * (non-Javadoc) - * - * @see javax.jcr.Repository#login(javax.jcr.Credentials) - */ public Session login(Credentials credentials) throws LoginException, RepositoryException { return this.login(credentials, null); } - /* - * (non-Javadoc) - * - * @see javax.jcr.Repository#login(java.lang.String) - */ public Session login(String workspace) throws LoginException, NoSuchWorkspaceException, RepositoryException { return this.login(null, workspace); } - /* - * (non-Javadoc) - * - * @see org.apache.sling.core.jcr.SlingRepository#login(javax.jcr.Credentials, - * java.lang.String) - */ public Session login(Credentials credentials, String workspace) throws LoginException, NoSuchWorkspaceException, RepositoryException { // if already stopped, don't retrieve a session - if (this.componentContext == null || this.getDelegatee() == null) { + if (this.componentContext == null || this.getRepository() == null) { throw new RepositoryException("Sling Repository not ready"); } @@ -243,13 +221,14 @@ } try { - getLog().log(LogService.LOG_DEBUG, - "Logging in to workspace '" + workspace + "'"); + log(LogService.LOG_DEBUG, "login: Logging in to workspace '" + + workspace + "'"); return this.getPoolManager().login(credentials, workspace); } catch (NoSuchWorkspaceException nswe) { // if the desired workspace is the default workspace, try to create // (but not if using the repository-supplied default workspace) - if (workspace!=null && workspace.equals(this.getDefaultWorkspace()) + if (workspace != null + && workspace.equals(this.getDefaultWorkspace()) && this.createWorkspace(workspace)) { return this.getPoolManager().login(credentials, workspace); } @@ -262,46 +241,284 @@ /* * (non-Javadoc) - * + * * @see javax.jcr.Repository#getDescriptor(java.lang.String) */ public String getDescriptor(String name) { - try { - return this.getDelegatee().getDescriptor(name); - } catch (RepositoryException re) { - this.log(LogService.LOG_ERROR, "Repository not available", re); + Repository repo = getRepository(); + if (repo != null) { + return repo.getDescriptor(name); } + log(LogService.LOG_ERROR, "getDescriptor: Repository not available"); return null; } /* * (non-Javadoc) - * + * * @see javax.jcr.Repository#getDescriptorKeys() */ public String[] getDescriptorKeys() { - try { - return this.getDelegatee().getDescriptorKeys(); - } catch (RepositoryException re) { - this.log(LogService.LOG_ERROR, "Repository not available", re); + Repository repo = getRepository(); + if (repo != null) { + return repo.getDescriptorKeys(); } + log(LogService.LOG_ERROR, "getDescriptorKeys: Repository not available"); return new String[0]; } - // ---------- logging ------------------------------------------------------ + //---------- Session Pool support ----------------------------------------- + + protected final SessionPoolManager getPoolManager() { + if (this.poolManager == null) { + this.poolManager = new SessionPoolManager(this.getRepository(), + this.loader, this.getSessionPoolFactory()); + } - protected abstract LogService getLog(); + return this.poolManager; + } + + /** + * @return + */ + protected SessionPoolFactory getSessionPoolFactory() { + @SuppressWarnings("unchecked") + Dictionary properties = this.componentContext.getProperties(); + final int maxActiveSessions = this.getIntProperty(properties, + PROPERTY_MAX_ACTIVE_SESSIONS); + final int maxIdleSessions = this.getIntProperty(properties, + PROPERTY_MAX_IDLE_SESSIONS); + final int maxActiveSessionsWait = this.getIntProperty(properties, + PROPERTY_MAX_ACTIVE_SESSIONS_WAIT); + return new SessionPoolFactory() { + + public SessionPool createPool(final SessionPoolManager mgr, + final SimpleCredentials credentials) { + // create and configure the new pool + final SessionPool pool = createSessionPool(mgr, credentials); + pool.setMaxActiveSessions(maxActiveSessions); + pool.setMaxActiveSessionsWait(maxActiveSessionsWait); + pool.setMaxIdleSessions(maxIdleSessions); + return pool; + } + }; + } + + protected SessionPool createSessionPool(final SessionPoolManager mgr, + final SimpleCredentials credentials) { + final SessionPool pool = new SessionPool(mgr, credentials); + return pool; + } + + // ---------- logging ------------------------------------------------------ protected void log(int level, String message) { this.log(level, message, null); } protected void log(int level, String message, Throwable t) { - LogService log = this.getLog(); + LogService log = this.log; if (log != null) { - log.log(this.componentContext.getServiceReference(), level, message, t); + log.log(this.componentContext.getServiceReference(), level, + message, t); + } + } + + // ---------- Repository Access ------------------------------------------- + + /** + * Acquires the repository by calling the + * {@link org.apache.sling.jcr.base.util.RepositoryAccessor#getRepositoryFromURL(String)} + * with the value of the + * {@link org.apache.sling.jcr.base.util.RepositoryAccessor#REPOSITORY_URL_OVERRIDE_PROPERTY} + * framework or configuration property. If the property exists and a + * repository can be accessed using this property, that repository is + * returned. Otherwise null is returned. + *

+ * Extensions of this class may overwrite this method with implementation + * specific acquisition semantics and may call this base class method or not + * as the implementation sees fit. + *

+ * 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. + */ + protected Repository acquireRepository() { + // if the environment provides a repository override URL, other settings + // are ignored + String overrideUrl = (String) componentContext.getProperties().get( + RepositoryAccessor.REPOSITORY_URL_OVERRIDE_PROPERTY); + if (overrideUrl == null) { + overrideUrl = componentContext.getBundleContext().getProperty( + RepositoryAccessor.REPOSITORY_URL_OVERRIDE_PROPERTY); + } + + if (overrideUrl != null && overrideUrl.length() > 0) { + log(LogService.LOG_INFO, + "acquireRepository: Will not use embedded repository due to property " + + RepositoryAccessor.REPOSITORY_URL_OVERRIDE_PROPERTY + "=" + + overrideUrl + ", acquiring repository using that URL"); + return new RepositoryAccessor().getRepositoryFromURL(overrideUrl); + } + + log(LogService.LOG_DEBUG, + "acquireRepository: No existing repository to access"); + return null; + } + + /** + * This method is called after a repository has been acquired by + * {@link #acquireRepository()} but before the repository is registered as a + * service. + *

+ * Implementations may overwrite this method but MUST call this base class + * implementation first. + * + * @param repository The JCR Repository to setup. + */ + protected void setupRepository(Repository repository) { + BundleContext bundleContext = componentContext.getBundleContext(); + this.loader = new Loader(this, bundleContext.getBundles()); + } + + /** + * Registers this component as an OSGi service with type + * javax.jcr.Repository and + * org.apache.sling.jcr.api.SlingRepository using the + * component properties as service registration properties. + *

+ * This method may be overwritten to register the component with different + * types. + * + * @return The OSGi ServiceRegistration object representing + * the registered service. + */ + protected ServiceRegistration registerService() { + @SuppressWarnings("unchecked") + Dictionary props = componentContext.getProperties(); + String[] interfaces = new String[] { SlingRepository.class.getName(), + Repository.class.getName() }; + + return componentContext.getBundleContext().registerService(interfaces, + this, props); + } + + /** + * Returns the repository underlying this instance or null if + * no repository is currently being available. + */ + protected Repository getRepository() { + return repository; + } + + /** + * Checks that the given repository is still available. This + * implementation tries to get the Repository.SPEC_NAME_DESC + * descriptor from the repository and returns true if the + * returned value is not null. + *

+ * Extensions of this class may overwrite this method to implement different + * access checks. The contract of this method must be obeyed, though in a + * sense, the true must only be returned if + * repository is actually usable. + * + * @param repository The JCR Repository to check for + * availability. + * @return true if repository is not + * null and accessible. + */ + protected boolean pingRepository(Repository repository) { + if (repository != null) { + try { + return repository.getDescriptor(Repository.SPEC_NAME_DESC) != null; + } catch (Throwable t) { + log(LogService.LOG_DEBUG, "pingRepository: Repository " + + repository + " does not seem to be available any more", t); + } + } + + // fall back to unavailable + return false; + } + + /** + * Unregisters the service represented by the + * serviceRegistration. + *

+ * This method may be overwritten by extensions of this class as long as it + * is made sure, the given service registration is unregistered. + */ + protected void unregisterService(ServiceRegistration serviceRegistration) { + serviceRegistration.unregister(); + } + + /** + * Performs any cleanups before the repository is actually disposed off by + * the {@link #disposeRepository(Repository)} method. + *

+ * This method is meant for cleanup tasks before the repository is actually + * disposed off. Extensions of this class may overwrite but must call this + * base class implementation. + * + * @param repository + */ + protected void tearDown(Repository repository) { + + if (this.poolManager != null) { + this.poolManager.dispose(); + this.poolManager = null; + } + + if (this.loader != null) { + this.loader.dispose(); + this.loader = null; + } + } + + /** + * Disposes off the given repository. This base class + * implementation does nothing. Extensions should overwrite if any special + * disposal operation is required. + * + * @param repository + */ + protected void disposeRepository(Repository repository) { + // nothing to do here ... + } + + //---------- SynchronousBundleListener ------------------------------------ + + /** + * 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 BundleEvent representing the bundle state + * change. + */ + public void bundleChanged(BundleEvent event) { + // Take care: This is synchronous - take care to not block the system !! + Loader theLoader = this.loader; + if (theLoader != null) { + switch (event.getType()) { + case BundleEvent.INSTALLED: + // register types when the bundle gets installed + theLoader.registerBundle(event.getBundle()); + break; + + case BundleEvent.UNINSTALLED: + theLoader.unregisterBundle(event.getBundle()); + break; + + case BundleEvent.UPDATED: + theLoader.updateBundle(event.getBundle()); + } } } @@ -313,6 +530,7 @@ /** * This method must be called if overwritten by implementations !! + * * @throws nothing, but allow derived classes to throw any Exception */ protected void activate(ComponentContext componentContext) throws Exception { @@ -320,8 +538,14 @@ @SuppressWarnings("unchecked") Dictionary properties = componentContext.getProperties(); - this.defaultWorkspace = this.getProperty(properties, PROPERTY_DEFAULT_WORKSPACE, - DEFAULT_WORKSPACE); + + // ensure the default workspace is not null and not empty + this.defaultWorkspace = this.getProperty(properties, + PROPERTY_DEFAULT_WORKSPACE, DEFAULT_WORKSPACE); + if (this.defaultWorkspace != null + && this.defaultWorkspace.length() == 0) { + this.defaultWorkspace = null; + } this.anonUser = this.getProperty(properties, PROPERTY_ANONYMOUS_USER, DEFAULT_ANONYMOUS_USER); @@ -333,77 +557,48 @@ this.adminPass = this.getProperty(properties, PROPERTY_ADMIN_PASS, DEFAULT_ADMIN_PASS).toCharArray(); - this.loader = new Loader(this); - + setPollTimeActive(getIntProperty(properties, PROPERTY_POLL_ACTIVE)); + setPollTimeInActive(getIntProperty(properties, PROPERTY_POLL_INACTIVE)); + componentContext.getBundleContext().addBundleListener(this); - // TODO: Consider running this in the background !! - // FIXME: Commented while SLING-132 is still unfixed - Bundle[] bundles = componentContext.getBundleContext().getBundles(); - for (int i = 0; i < bundles.length; i++) { - if ((bundles[i].getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED)) == 0) { - // load content for bundles which are neither INSTALLED nor - // UNINSTALLED - this.loader.registerBundle(bundles[i]); - } - } + startRepositoryPinger(); } /** * This method must be called if overwritten by implementations !! - * + * * @param componentContext */ protected void deactivate(ComponentContext componentContext) { componentContext.getBundleContext().removeBundleListener(this); - if (this.poolManager != null) { - this.poolManager.dispose(); - this.poolManager = null; - } - if ( this.loader != null ) { - this.loader.dispose(); - this.loader = null; - } + stopRepositoryPinger(); this.componentContext = null; } - - /** - * 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 BundleEvent representing the bundle state - * change. - */ - public void bundleChanged(BundleEvent event) { - // TODO: This is synchronous - take care to not block the system !! - switch (event.getType()) { - case BundleEvent.INSTALLED: - // register content and types when the bundle content is - // available - this.loader.registerBundle(event.getBundle()); - break; - - case BundleEvent.UNINSTALLED: - this.loader.unregisterBundle(event.getBundle()); - break; - case BundleEvent.UPDATED: - this.loader.updateBundle(event.getBundle()); + + protected void bindLog(LogService log) { + this.log = log; + } + + protected void unbindLog(LogService log) { + if (this.log == log) { + log = null; } } // ---------- internal ----------------------------------------------------- - private String getProperty(Dictionary properties, String name, - String defaultValue) { + private String getProperty(Dictionary properties, + String name, String defaultValue) { Object prop = properties.get(name); return (prop instanceof String) ? (String) prop : defaultValue; } - private int getIntProperty(Dictionary properties, String name) { + private int getIntProperty(Dictionary properties, + String name) { Object prop = properties.get(name); if (prop instanceof Number) { return ((Number) prop).intValue(); @@ -419,13 +614,14 @@ } private boolean createWorkspace(String workspace) { - this.log(LogService.LOG_INFO, "login: Requested workspace " + workspace - + " does not exist, trying to create"); + this.log(LogService.LOG_INFO, "createWorkspace: Requested workspace " + + workspace + " does not exist, trying to create"); Session tmpSession = null; try { - SimpleCredentials sc = new SimpleCredentials(this.adminUser, this.adminPass); - tmpSession = this.getDelegatee().login(sc); + SimpleCredentials sc = new SimpleCredentials(this.adminUser, + this.adminPass); + tmpSession = this.getRepository().login(sc); Workspace defaultWs = tmpSession.getWorkspace(); if (defaultWs instanceof JackrabbitWorkspace) { ((JackrabbitWorkspace) defaultWs).createWorkspace(workspace); @@ -433,11 +629,13 @@ } // not Jackrabbit - this.log(LogService.LOG_ERROR, "login: Cannot create requested workspace " - + workspace + ": Jackrabbit is required"); + this.log(LogService.LOG_ERROR, + "createWorkspace: Cannot create requested workspace " + + workspace + ": Jackrabbit is required"); } catch (Throwable t) { - this.log(LogService.LOG_ERROR, "login: Cannot create requested workspace " - + workspace, t); + this.log(LogService.LOG_ERROR, + "createWorkspace: Cannot create requested workspace " + + workspace, t); } finally { if (tmpSession != null) { tmpSession.logout(); @@ -447,4 +645,186 @@ // fall back to failure return false; } + + // ---------- Background operation checking repository availability -------- + + private void setPollTimeActive(int seconds) { + if (seconds < MIN_POLL) { + seconds = DEFAULT_POLL_ACTIVE; + } + pollTimeActive = 1000L * seconds; + } + + private void setPollTimeInActive(int seconds) { + if (seconds < MIN_POLL) { + seconds = DEFAULT_POLL_INACTIVE; + } + pollTimeInActive = 1000L * seconds; + } + + private void startRepositoryPinger() { + if (repositoryPinger != null) { + // make sure the ping will be running + running = true; + + // create and start the thread + repositoryPinger = new Thread(this, "Repository Pinger"); + repositoryPinger.start(); + } + } + + private void stopRepositoryPinger() { + + // make sure the thread is terminating + running = false; + + // nothing to do if the thread is not running at all + Thread rpThread = repositoryPinger; + if (rpThread == null) { + return; + } + + // clear the repositoryPinger thread field + repositoryPinger = null; + + // notify the thread for it to be able to shut down + synchronized (rpThread) { + rpThread.notifyAll(); + } + + // wait at most 10 seconds for the thread to terminate + try { + rpThread.join(10000L); + } catch (InterruptedException ie) { + // don't care here + } + + // consider it an error if the thread is still running !! + if (rpThread.isAlive()) { + log(LogService.LOG_ERROR, + "stopRepositoryPinger: Timed waiting for thread " + + rpThread + " to terminate"); + } + + } + + private boolean startRepository() { + try { + Repository newRepo = acquireRepository(); + if (newRepo != null) { + + // ensure we really have the repository + if (pingRepository(newRepo)) { + repository = newRepo; + + setupRepository(newRepo); + repositoryService = registerService(); + + return true; + } + + // otherwise let go off the repository and fail startup + log(LogService.LOG_INFO, "startRepository: Acquired Repository is not responsive, failing"); + disposeRepository(repository); + } + } catch (Throwable t) { + // consider an uncaught problem an error + log( + LogService.LOG_ERROR, + "startRepository: Uncaught problem trying to provide repository access", + t); + + // repository might be partially start, stop anything left + stopRepository(); + } + + return false; + } + + private void stopRepository() { + if (repositoryService != null) { + try { + unregisterService(repositoryService); + } catch (Throwable t) { + log( + LogService.LOG_INFO, + "stopRepository: Uncaught problem unregistering the repository service", + t); + } + repositoryService = null; + } + + if (repository != null) { + Repository oldRepo = repository; + repository = null; + + try { + tearDown(oldRepo); + } catch (Throwable t) { + log( + LogService.LOG_INFO, + "stopRepository: Uncaught problem tearing down the repository", + t); + } + + try { + disposeRepository(oldRepo); + } catch (Throwable t) { + log( + LogService.LOG_INFO, + "stopRepository: Uncaught problem disposing the repository", + t); + } + } + } + + public void run() { + long pollTime = pollTimeInActive; + Object waitLock = repositoryPinger; + + try { + + while (running) { + + Repository repo = repository; + if (repo == null && running) { + + if (startRepository()) { + pollTime = pollTimeActive; + } + + } else if (!pingRepository(repo)) { + + log(LogService.LOG_INFO, + "run: Repository not accessible any more, unregistering service"); + stopRepository(); + pollTime = pollTimeInActive; + + } else { + + log(LogService.LOG_DEBUG, + "run: Repository available, checking again in " + + pollTime + "ms"); + } + + + // only wait if still running + synchronized (waitLock) { + if (running) { + try { + waitLock.wait(pollTime); + } catch (InterruptedException ie) { + // don't care, go ahead + } + } + } + } + + } finally { + // whatever goes on, make sure the repository is disposed off + // at the end of the thread.... + stopRepository(); + } + } + } Modified: incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/internal/SessionPool.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/internal/SessionPool.java?rev=633823&r1=633822&r2=633823&view=diff ============================================================================== --- incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/internal/SessionPool.java (original) +++ incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/internal/SessionPool.java Wed Mar 5 03:35:06 2008 @@ -117,7 +117,7 @@ * sessions from this pool to drop below the configured number of maximum * active sessions. * - * @see #PARAM_MAX_ACTIVE_SESSIONS_WAIT + * @see #PROPERTY_MAX_ACTIVE_SESSIONS_WAIT * @see #DEFAULT_MAX_ACTIVE_SESSIONS_WAIT * @see #getMaxActiveSessionsWait() * @see #setMaxActiveSessionsWait(long) Modified: incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/internal/loader/Loader.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/internal/loader/Loader.java?rev=633823&r1=633822&r2=633823&view=diff ============================================================================== --- incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/internal/loader/Loader.java (original) +++ incubator/sling/trunk/jcr/base/src/main/java/org/apache/sling/jcr/base/internal/loader/Loader.java Wed Mar 5 03:35:06 2008 @@ -61,9 +61,19 @@ /** Namespace prefix table. */ private final Map namespaceTable = new HashMap(); - public Loader(AbstractSlingRepository repository) { + public Loader(AbstractSlingRepository repository, Bundle[] existingBundles) { this.slingRepository = repository; this.delayedBundles = new LinkedList(); + + // scan existing bundles + for (Bundle bundle : existingBundles) { + if ((bundle.getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED)) == 0) { + // load content for bundles which are neither INSTALLED nor + // UNINSTALLED + registerBundle(bundle); + } + } + } public void dispose() {