You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/18 23:21:35 UTC
[sling-org-apache-sling-jcr-registration] 02/33: SLING-2352 Finish
migration of the Repository Registration Support out of
jackrabbit-jcr-server into its own bundle
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-registration.git
commit 9b764f225f934763d70134085ebfcc1e1541ded7
Author: Felix Meschberger <fm...@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@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" <co...@sling.apache.org>.