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>.