This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.jcr.registration-1.0.0
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-registration.git

commit 5c27e9f0381204c0b533dc93dfd52453a1e0bcfc
Author: Felix Meschberger <fmesc...@apache.org>
AuthorDate: Wed May 2 13:22:32 2012 +0000

    SLING-2352 Finish migration of the Repository Registration Support out of 
jackrabbit-jcr-server into its own bundle
    
    git-svn-id: 
https://svn.apache.org/repos/asf/sling/trunk/bundles/jcr/registration@1333033 
13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            |  28 +-
 .../registration/AbstractRegistrationSupport.java  | 386 +++++++++++++++++++++
 .../registration/impl/JndiRegistrationSupport.java | 165 +++++++++
 .../registration/impl/RmiRegistrationSupport.java  | 294 ++++++++++++++++
 .../sling/jcr/registration/package-info.java       |  31 ++
 .../OSGI-INF/metatype/metatype.properties          |  54 +++
 6 files changed, 951 insertions(+), 7 deletions(-)

diff --git a/pom.xml b/pom.xml
index a7146fa..ec5967b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,23 +51,23 @@
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.3.7</version>
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
                         <Bundle-Category>
                             sling,jcr
                         </Bundle-Category>
-                        <Private-Package>
-                            org.apache.sling.jcr.registrar.impl.*
-                        </Private-Package>
-                        <Export-Package>
-                            org.apache.sling.jcr.registrar
-                        </Export-Package>
+                        <Import-Package>
+                            javax.transaction.xa;resolution:=optional,
+                            *
+                        </Import-Package>
                         <DynamicImport-Package>
                             *
                         </DynamicImport-Package>
                         <Embed-Dependency>
-                            
jackrabbit-core;inline:=org/apache/jackrabbit/core/jndi/provider/*.class
+                            
jackrabbit-core;inline=org/apache/jackrabbit/core/jndi/provider/*.class,
+                            
jackrabbit-jcr-rmi;inline=org/apache/jackrabbit/rmi/iterator/**|org/apache/jackrabbit/rmi/observation/**|org/apache/jackrabbit/rmi/remote/**|org/apache/jackrabbit/rmi/server/**|org/apache/jackrabbit/rmi/value/**|org/apache/jackrabbit/rmi/server/**
                         </Embed-Dependency>
                     </instructions>
                 </configuration>
@@ -79,11 +79,19 @@
         <dependency>
             <groupId>javax.jcr</groupId>
             <artifactId>jcr</artifactId>
+            <version>2.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.scr.annotations</artifactId>
         </dependency>
+        <dependency>
+            <groupId>biz.aQute</groupId>
+            <artifactId>bndlib</artifactId>
+            <version>1.50.0</version>
+            <scope>provided</scope>
+        </dependency>
+        
         <!-- OSGi Libraries -->
         <dependency>
             <groupId>org.osgi</groupId>
@@ -101,6 +109,12 @@
             <version>2.2.9</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>jackrabbit-jcr-rmi</artifactId>
+            <version>2.0.0</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git 
a/src/main/java/org/apache/sling/jcr/registration/AbstractRegistrationSupport.java
 
b/src/main/java/org/apache/sling/jcr/registration/AbstractRegistrationSupport.java
new file mode 100644
index 0000000..6d941ed
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/jcr/registration/AbstractRegistrationSupport.java
@@ -0,0 +1,386 @@
+/*
+ * 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);
+        }
+    }
+
+}
diff --git 
a/src/main/java/org/apache/sling/jcr/registration/impl/JndiRegistrationSupport.java
 
b/src/main/java/org/apache/sling/jcr/registration/impl/JndiRegistrationSupport.java
new file mode 100644
index 0000000..32c5fb4
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/jcr/registration/impl/JndiRegistrationSupport.java
@@ -0,0 +1,165 @@
+/*
+ * 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.impl;
+
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Properties;
+
+import javax.jcr.Repository;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.sling.jcr.registration.AbstractRegistrationSupport;
+import org.osgi.service.log.LogService;
+
+
+/**
+ * The <code>JndiRegistrationSupport</code> extends the
+ * {@link AbstractRegistrationSupport} class to register repositories with a
+ * JNDI context whose provider URL and initial factory class name may be
+ * configured.
+ * <p>
+ * Note: Currently, only these two properties are declared to be configurable,
+ * in the future a mechanism should be devised to support declaration of more
+ * properties.
+ */
+@Component(
+        immediate = true,
+        metatype = true,
+        label = "%jndi.name",
+        description = "%jndi.description",
+        name = 
"org.apache.sling.jcr.jackrabbit.server.JndiRegistrationSupport")
+@org.apache.felix.scr.annotations.Properties({
+    @Property(
+            name = "java.naming.factory.initial",
+            value = 
"org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory",
+            label = "%jndi.factory.name",
+            description = "%jndi.factory.description"),
+    @Property(
+            name = "java.naming.provider.url",
+            value = "http://sling.apache.org";,
+            label = "%jndi.providerurl.name",
+            description = "%jndi.providerurl.description"),
+    @Property(name = "service.vendor", value = "The Apache Software 
Foundation", propertyPrivate = true),
+    @Property(name = "service.description", value = "JNDI Repository 
Registration", propertyPrivate = true)
+})
+public class JndiRegistrationSupport extends AbstractRegistrationSupport {
+
+    private Context jndiContext;
+
+    // ---------- SCR intergration 
---------------------------------------------
+
+    protected boolean doActivate() {
+        @SuppressWarnings("unchecked")
+        Dictionary<String, Object> props = 
this.getComponentContext().getProperties();
+        Properties env = new Properties();
+        for (Enumeration<String> pe = props.keys(); pe.hasMoreElements();) {
+            String key = pe.nextElement();
+            if (key.startsWith("java.naming.")) {
+                env.setProperty(key, (String) props.get(key));
+            }
+        }
+
+        try {
+            // create the JNDI context for registration
+            this.jndiContext = this.createInitialContext(env);
+
+            this.log(LogService.LOG_INFO, "Using JNDI context "
+                + this.jndiContext.getEnvironment() + " to register 
repositories",
+                null);
+
+            return true;
+        } catch (NamingException ne) {
+            this.log(
+                LogService.LOG_ERROR,
+                "Problem setting up JNDI initial context, repositories will 
not be registered. Reason: "
+                    + ne.getMessage(), null);
+        }
+
+        // fallback to false
+        return false;
+    }
+
+    protected void doDeactivate() {
+        if (this.jndiContext != null) {
+            try {
+                this.jndiContext.close();
+            } catch (NamingException ne) {
+                this.log(LogService.LOG_INFO, "Problem closing JNDI context", 
ne);
+            }
+
+            this.jndiContext = null;
+        }
+    }
+
+    private Context createInitialContext(final Properties env) throws 
NamingException {
+        try {
+            return AccessController.doPrivileged(new 
PrivilegedExceptionAction<Context>() {
+                public Context run() throws NamingException {
+                    Thread currentThread = Thread.currentThread();
+                    ClassLoader old = currentThread.getContextClassLoader();
+                    
currentThread.setContextClassLoader(JndiRegistrationSupport.this.getClass().getClassLoader());
+                    try {
+                        return new InitialContext(env);
+                    } finally {
+                        currentThread.setContextClassLoader(old);
+                    }
+                }
+            });
+        } catch (PrivilegedActionException pae) {
+            // we now that this method only throws a NamingException
+            throw (NamingException) pae.getCause();
+        }
+    }
+
+    protected Object bindRepository(String name, Repository repository) {
+
+        if (this.jndiContext != null) {
+            try {
+                this.jndiContext.bind(name, repository);
+                this.log(LogService.LOG_INFO, "Repository bound to JNDI as " + 
name,
+                    null);
+                return repository;
+            } catch (NamingException ne) {
+                this.log(LogService.LOG_ERROR, "Failed to register repository 
" + name, ne);
+            }
+        }
+
+        // fall back to unregistered in case of failures or no context
+        return null;
+    }
+
+    protected void unbindRepository(String name, Object data) {
+        if (this.jndiContext != null) {
+            try {
+                this.jndiContext.unbind(name);
+                this.log(LogService.LOG_INFO, "Repository " + name
+                    + " unbound from JNDI", null);
+            } catch (NamingException ne) {
+                this.log(LogService.LOG_ERROR, "Problem unregistering 
repository "
+                    + name, ne);
+            }
+        }
+    }
+}
diff --git 
a/src/main/java/org/apache/sling/jcr/registration/impl/RmiRegistrationSupport.java
 
b/src/main/java/org/apache/sling/jcr/registration/impl/RmiRegistrationSupport.java
new file mode 100644
index 0000000..de0dad7
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/jcr/registration/impl/RmiRegistrationSupport.java
@@ -0,0 +1,294 @@
+/*
+ * 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.impl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.rmi.NoSuchObjectException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+
+import javax.jcr.Repository;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.jackrabbit.rmi.server.RemoteAdapterFactory;
+import org.apache.jackrabbit.rmi.server.ServerAdapterFactory;
+import org.apache.sling.jcr.registration.AbstractRegistrationSupport;
+import org.osgi.service.log.LogService;
+
+/**
+ * The <code>RmiRegistrationSupport</code> extends the
+ * {@link AbstractRegistrationSupport} class to register repositories with an
+ * RMI registry whose provider localhost port may be configured.
+ * <p>
+ * Note: Currently only registries in this Java VM are supported. In the future
+ * support for external registries may be added.
+ */
+@Component(
+        immediate = true,
+        metatype = true,
+        label = "%rmi.name",
+        description = "%rmi.description",
+        name = "org.apache.sling.jcr.jackrabbit.server.RmiRegistrationSupport")
+@org.apache.felix.scr.annotations.Properties({
+    @Property(name = "service.vendor", value = "The Apache Software 
Foundation", propertyPrivate = true),
+    @Property(name = "service.description", value = "RMI based Repository 
Registration", propertyPrivate = true)
+})
+public class RmiRegistrationSupport extends AbstractRegistrationSupport {
+
+    @Property(intValue = 1099, label = "%rmi.port.name", description = 
"%rmi.port.description")
+    public static final String PROP_REGISTRY_PORT = "port";
+
+    private int registryPort;
+
+    /** The private RMI registry, only defined if possible */
+    private Registry registry;
+
+    private boolean registryIsPrivate;
+
+    // ---------- SCR intergration 
---------------------------------------------
+
+    /**
+     * Read the registry port from the configuration properties. If the value 
is
+     * invalid (higher than 65525), the RMI registry is disabled. Likewise the
+     * registry is disabled, if the port property is negative. If the port is
+     * zero or not a number, the default port (1099) is assumed.
+     */
+    protected boolean doActivate() {
+
+        Object portProp = this.getComponentContext().getProperties().get(
+            PROP_REGISTRY_PORT);
+        if (portProp instanceof Number) {
+            this.registryPort = ((Number) portProp).intValue();
+        } else {
+            try {
+                this.registryPort = Integer.parseInt(String.valueOf(portProp));
+            } catch (NumberFormatException nfe) {
+                this.registryPort = 0;
+            }
+        }
+
+        // ensure correct value
+        if (this.registryPort < 0) {
+            this.log(LogService.LOG_WARNING,
+                "RMI registry disabled (no or negative RMI port configured)",
+                null);
+            return false;
+        } else if (this.registryPort == 0) {
+            this.registryPort = Registry.REGISTRY_PORT;
+        } else if (this.registryPort == 0 || this.registryPort > 0xffff) {
+            this.log(LogService.LOG_WARNING,
+                "Illegal RMI registry port number " + this.registryPort
+                    + ", disabling RMI registry", null);
+            return false;
+        }
+
+        this.log(LogService.LOG_INFO, "Using RMI Registry port "
+            + this.registryPort, null);
+        return true;
+    }
+
+    /**
+     * If a private registry has been acquired this method unexports the
+     * registry object to free the RMI registry OID for later use.
+     */
+    protected void doDeactivate() {
+        // if we have a private RMI registry, unexport it here to free
+        // the RMI registry OID
+        if (this.registry != null && this.registryIsPrivate) {
+            try {
+                UnicastRemoteObject.unexportObject(this.registry, true);
+                this.log(LogService.LOG_INFO,
+                    "Unexported private RMI Registry at " + this.registryPort,
+                    null);
+            } catch (NoSuchObjectException nsoe) {
+                // not expected, but don't really care either
+                this.log(LogService.LOG_INFO,
+                    "Cannot unexport private RMI Registry reference", nsoe);
+            }
+        }
+        this.registry = null;
+    }
+
+    protected Object bindRepository(String name, Repository repository) {
+        return new RmiRegistration(name, repository);
+    }
+
+    protected void unbindRepository(String name, Object data) {
+        RmiRegistration rr = (RmiRegistration) data;
+        rr.unregister();
+    }
+
+    // ---------- support for private rmi registries 
---------------------------
+
+    /**
+     * Tries to create a private registry at the configured port. If this fails
+     * (for example because a registry already exists in the VM, a registry 
stub
+     * for the port is returned. This latter stub may or may not connect to a
+     * real registry, which may only be found out, when trying to register
+     * repositories.
+     */
+    private Registry getPrivateRegistry() {
+        if (this.registry == null) {
+            try {
+                // no, so try to create first
+                this.registry = 
LocateRegistry.createRegistry(this.registryPort);
+                this.registryIsPrivate = true;
+                this.log(LogService.LOG_INFO, "Using private RMI Registry at "
+                    + this.registryPort, null);
+
+            } catch (RemoteException re) {
+                // creating failed, check whether there is already one
+                this.log(LogService.LOG_INFO,
+                    "Cannot create private registry, trying existing registry 
at "
+                        + this.registryPort + ", reason: " + re.toString(),
+                    null);
+
+                try {
+                    this.registry = 
LocateRegistry.getRegistry(this.registryPort);
+                    this.registryIsPrivate = false;
+                    this.log(LogService.LOG_INFO,
+                        "Trying existing registry at " + this.registryPort,
+                        null);
+
+                } catch (RemoteException pre) {
+                    this.log(
+                        LogService.LOG_ERROR,
+                        "Cannot get existing registry, will not register 
repositories on RMI",
+                        pre);
+                }
+            }
+        }
+
+        return this.registry;
+    }
+
+    /**
+     * Returns a Jackrabbit JCR RMI <code>RemoteAdapterFactory</code> to be
+     * used to publish local (server-side) JCR objects to a remote client.
+     * <p>
+     * This method returns an instance of the
+     * <code>JackrabbitServerAdapterFactory</code> class to enable the use of
+     * the Jackrabbit API over RMI. Extensions of this class may overwrite this
+     * method to return a different implementation to provide different JCR
+     * extension API depending on the server implementation.
+     */
+    protected RemoteAdapterFactory getRemoteAdapterFactory() {
+        return new ServerAdapterFactory();
+    }
+
+    // ---------- Inner Class 
--------------------------------------------------
+
+    private class RmiRegistration {
+
+        private String rmiName;
+
+        private Remote rmiRepository;
+
+        RmiRegistration(String rmiName, Repository repository) {
+            this.register(rmiName, repository);
+        }
+
+        public String getRmiName() {
+            return this.rmiName;
+        }
+
+        public String getRmiURL() {
+            String host;
+            try {
+                host = InetAddress.getLocalHost().getCanonicalHostName();
+            } catch (IOException ignore) {
+                host = "localhost";
+            }
+            return "//" + host + ":" + RmiRegistrationSupport.this.registryPort
+                + "/" + this.getRmiName();
+        }
+
+        private void register(String rmiName, Repository repository) {
+            System.setProperty("java.rmi.server.useCodebaseOnly", "true");
+
+            // try to create remote repository and keep it to ensure it is
+            // unexported in the unregister() method
+            try {
+                RemoteAdapterFactory raf = getRemoteAdapterFactory();
+                this.rmiRepository = raf.getRemoteRepository(repository);
+            } catch (RemoteException e) {
+                RmiRegistrationSupport.this.log(LogService.LOG_ERROR,
+                    "Unable to create remote repository.", e);
+                return;
+            } catch (Exception e) {
+                RmiRegistrationSupport.this.log(
+                    LogService.LOG_ERROR,
+                    "Unable to create RMI repository. jcr-rmi.jar might be 
missing.",
+                    e);
+                return;
+            }
+
+            try {
+                // check whether we have a private registry already
+                Registry registry = 
RmiRegistrationSupport.this.getPrivateRegistry();
+                if (registry != null) {
+                    registry.bind(rmiName, this.rmiRepository);
+                    this.rmiName = rmiName;
+                    RmiRegistrationSupport.this.log(LogService.LOG_INFO,
+                        "Repository bound to " + this.getRmiURL(), null);
+                }
+
+            } catch (NoSuchObjectException nsoe) {
+                // the registry does not really exist
+                RmiRegistrationSupport.this.log(LogService.LOG_WARNING,
+                    "Cannot contact RMI registry at "
+                        + RmiRegistrationSupport.this.registryPort
+                        + ", repository not registered", null);
+            } catch (Exception e) {
+                RmiRegistrationSupport.this.log(LogService.LOG_ERROR,
+                    "Unable to bind repository via RMI.", e);
+            }
+        }
+
+        public void unregister() {
+            // unregister repository
+            if (this.rmiName != null) {
+                try {
+                    RmiRegistrationSupport.this.getPrivateRegistry().unbind(
+                        this.rmiName);
+                    RmiRegistrationSupport.this.log(LogService.LOG_INFO,
+                        "Repository unbound from " + this.getRmiURL(), null);
+                } catch (Exception e) {
+                    RmiRegistrationSupport.this.log(LogService.LOG_ERROR,
+                        "Error while unbinding repository from JNDI: " + e,
+                        null);
+                }
+            }
+
+            // drop strong reference to remote repository
+            if (this.rmiRepository != null) {
+                try {
+                    UnicastRemoteObject.unexportObject(this.rmiRepository, 
true);
+                } catch (NoSuchObjectException nsoe) {
+                    // not expected, but don't really care either
+                    RmiRegistrationSupport.this.log(LogService.LOG_INFO,
+                        "Cannot unexport remote Repository reference", nsoe);
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/jcr/registration/package-info.java 
b/src/main/java/org/apache/sling/jcr/registration/package-info.java
new file mode 100644
index 0000000..1a1f77f
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/registration/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+/**
+ * The <code>org.apache.sling.jcr.registration</code> package exports
+ * the {@link org.apache.sling.jcr.registration.AbstractRegistrationSupport}
+ * class which may be extended by service exposing JCR Repository services
+ * in any one non-OSGi registry such as RMI or JNDI.
+ */
+@Version("1.0")
+@Export(optional = "provide:=true")
+package org.apache.sling.jcr.registration;
+
+import aQute.bnd.annotation.Export;
+import aQute.bnd.annotation.Version;
diff --git a/src/main/resources/OSGI-INF/metatype/metatype.properties 
b/src/main/resources/OSGI-INF/metatype/metatype.properties
new file mode 100644
index 0000000..cf44eab
--- /dev/null
+++ b/src/main/resources/OSGI-INF/metatype/metatype.properties
@@ -0,0 +1,54 @@
+#
+#  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.
+#
+
+#
+# This file contains localization strings for configuration labels and
+# descriptions as used in the metatype.xml descriptor generated by the
+# the Sling SCR plugin
+
+#
+# JNDI Registration Support
+jndi.name = Apache Sling JCR Repository JNDI Registrar
+jndi.description = The JNDI Registrar listens for embedded repositories \
+ to be registered as services and registers them in the JNDI context under the 
\
+ name specified in the "name" service property.
+
+jndi.factory.name = Initial Context Factory
+jndi.factory.description = The fully qualified class name of the factory class 
\
+ that will create an initial context.
+     
+jndi.providerurl.name = Provider URL
+jndi.providerurl.description =  An URL string for the service provider (e.g. \
+ "ldap://somehost:389";).
+     
+#
+# RMI Registration Support
+rmi.name = Apache Sling JCR Repository RMI Registrar
+rmi.description = The RMI Registrar listens for embedded repositories \
+ to be registered as services and registers them in an RMI registry under the \
+ name specified in the "name" service property.
+
+rmi.port.name = Port Number
+rmi.port.description = Port number of the RMI registry to use. The RMI 
Registrar \
+ first tries to create a private RMI registry at this port. If this fails, an \
+ existing registry is tried to connect at this port on local host. If this \
+ number is negative, the RMI Registrar is disabled. If this number is higher \
+ than 65535, an error message is logged and the RMI Registrar is also \
+ disabled. If this number is zero, the system default RMI Registry port 1099 \
+ is used.

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <commits@sling.apache.org>.

Reply via email to