/*
* 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.
*/
/**
* @author Vasily Zakharov
* @version $Revision: 1.1.2.4 $
*/
package org.apache.harmony.jndi.provider.rmi.registry;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.ConnectException;
import java.rmi.ConnectIOException;
import java.rmi.MarshalException;
import java.rmi.NoSuchObjectException;
import java.rmi.NotBoundException;
import java.rmi.RMISecurityManager;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.ServerException;
import java.rmi.StubNotFoundException;
import java.rmi.UnknownHostException;
import java.rmi.UnmarshalException;
import java.rmi.activation.ActivateFailedException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.ExportException;
import java.rmi.server.RMIClientSocketFactory;
import java.util.Hashtable;
import javax.naming.Binding;
import javax.naming.CommunicationException;
import javax.naming.CompositeName;
import javax.naming.ConfigurationException;
import javax.naming.Context;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.NameAlreadyBoundException;
import javax.naming.NameClassPair;
import javax.naming.NameNotFoundException;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.NoPermissionException;
import javax.naming.NotContextException;
import javax.naming.OperationNotSupportedException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.ServiceUnavailableException;
import javax.naming.StringRefAddr;
import javax.naming.spi.NamingManager;
import org.apache.harmony.jndi.internal.nls.Messages;
/**
* RMI Registry context implementation.
*/
public class RegistryContext implements Context, Referenceable {
/**
* System property used to state whether the RMI security manager should be
* installed.
*/
public static final String SECURITY_MANAGER = "java.naming.rmi.security.manager"; //$NON-NLS-1$
/**
* System property used to supply the name of the
* {@link RMIClientSocketFactory} to use.
*/
public static final String CLIENT_SOCKET_FACTORY = "org.apache.harmony.jndi.provider.rmi.registry.clientSocketFactory"; //$NON-NLS-1$
/**
* Prefix for RMI URLs.
*/
public static final String RMI_URL_PREFIX = "rmi:"; //$NON-NLS-1$
/**
* Address type for RMI context references.
*/
public static final String ADDRESS_TYPE = "URL"; //$NON-NLS-1$
/**
* Name parser.
*/
protected static final NameParser nameParser = new AtomicNameParser();
/**
* Registry host, stored to produce copies of this context.
*/
protected String host;
/**
* Registry port, stored to produce copies of this context.
*/
protected int port;
/**
* RMI client socket factory, stored to produce copies of this context.
*/
protected RMIClientSocketFactory csf;
/**
* Local environment.
*/
protected Hashtable<Object, Object> environment;
/**
* RMI Registry.
*/
protected Registry registry;
/**
* Reference for this context, initialized by
* {@link RegistryContextFactory#getObjectInstance(Object, Name, Context, Hashtable)}.
*/
protected Reference reference;
/**
* Creates RMI registry context bound to RMI Registry operating on the
* specified host and port.
*
* @param host
* Host. If <code>null</code>, localhost is assumed.
*
* @param port
* Port. If <code>0</code>, default registry port (<code>1099</code>)
* is assumed.
*
* @param environment
* Context environment, may be <code>null</code> which denotes
* empty environment.
*
* @throws NamingException
* If some naming error occurs.
*/
@SuppressWarnings("unchecked")
public RegistryContext(String host, int port, Hashtable<?, ?> environment)
throws NamingException {
this.host = host;
this.port = port;
this.environment = ((environment != null) ? (Hashtable<Object, Object>) environment
.clone()
: new Hashtable<Object, Object>());
if (this.environment.get(SECURITY_MANAGER) != null) {
installSecurityManager();
}
String clientSocketFactoryName = (String) this.environment
.get(CLIENT_SOCKET_FACTORY);
if (clientSocketFactoryName == null) {
csf = null;
} else {
try {
csf = (RMIClientSocketFactory) Class.forName(
clientSocketFactoryName, true,
Thread.currentThread().getContextClassLoader())
.newInstance();
} catch (ClassNotFoundException e) {
// jndi.79=RMI Client Socket Factory cannot be instantiated
throw (ConfigurationException) new ConfigurationException(
Messages.getString("jndi.79")) //$NON-NLS-1$
.initCause(e);
} catch (InstantiationException e) {
// jndi.79=RMI Client Socket Factory cannot be instantiated
throw (ConfigurationException) new ConfigurationException(
Messages.getString("jndi.79")) //$NON-NLS-1$
.initCause(e);
} catch (IllegalAccessException e) {
// jndi.79=RMI Client Socket Factory cannot be instantiated
throw (NoPermissionException) new NoPermissionException(
Messages.getString("jndi.79")) //$NON-NLS-1$
.initCause(e);
}
}
registry = getRegistry(host, port, csf);
reference = null;
}
/**
* Creates RMI registry context by copying the specified context.
*
* @param context
* Context to copy.
*/
@SuppressWarnings("unchecked")
protected RegistryContext(RegistryContext context) {
host = context.host;
port = context.port;
csf = context.csf;
environment = (Hashtable<Object, Object>) context.environment.clone();
registry = context.registry;
reference = context.reference;
}
/**
* {@inheritDoc}
*/
public Object lookup(Name name) throws NamingException {
if (name.isEmpty()) {
return cloneContext();
}
String stringName = getMyComponents(name);
try {
return getObjectInstance(stringName, registry.lookup(stringName));
} catch (NotBoundException e) {
// jndi.7A=Name is not bound: {0}
throw (NameNotFoundException) new NameNotFoundException(Messages
.getString("jndi.7A", stringName)).initCause(e); //$NON-NLS-1$
} catch (RemoteException e) {
throw (NamingException) newNamingException(e).fillInStackTrace();
}
}
/**
* {@inheritDoc}
*/
public Object lookup(String name) throws NamingException {
return lookup(new CompositeName(name));
}
/**
* {@inheritDoc}
*/
public Object lookupLink(Name name) throws NamingException {
return lookup(name);
}
/**
* {@inheritDoc}
*/
public Object lookupLink(String name) throws NamingException {
return lookupLink(new CompositeName(name));
}
/**
* {@inheritDoc}
*/
public void bind(Name name, Object obj) throws NamingException {
if (name.isEmpty()) {
// jndi.7B=Cannot bind empty name
throw new InvalidNameException(Messages.getString("jndi.7B")); //$NON-NLS-1$
}
String stringName = getMyComponents(name);
try {
registry.bind(stringName, getStateToBind(stringName, obj));
} catch (AlreadyBoundException e) {
// jndi.7C=Name is already bound: {0}
throw (NameAlreadyBoundException) new NameAlreadyBoundException(
Messages.getString("jndi.7C", stringName)).initCause(e); //$NON-NLS-1$
} catch (RemoteException e) {
throw (NamingException) newNamingException(e).fillInStackTrace();
}
}
/**
* {@inheritDoc}
*/
public void bind(String name, Object obj) throws NamingException {
bind(new CompositeName(name), obj);
}
/**
* {@inheritDoc}
*/
public void rebind(Name name, Object obj) throws NamingException {
if (name.isEmpty()) {
// jndi.7D=Cannot rebind empty name
throw new InvalidNameException(Messages.getString("jndi.7D")); //$NON-NLS-1$
}
String stringName = getMyComponents(name);
try {
registry.rebind(stringName, getStateToBind(stringName, obj));
} catch (RemoteException e) {
throw (NamingException) newNamingException(e).fillInStackTrace();
}
}
/**
* {@inheritDoc}
*/
public void rebind(String name, Object obj) throws NamingException {
rebind(new CompositeName(name), obj);
}
/**
* {@inheritDoc}
*/
public void unbind(Name name) throws NamingException {
if (name.isEmpty()) {
// jndi.7E=Cannot unbind empty name
throw new InvalidNameException(Messages.getString("jndi.7E")); //$NON-NLS-1$
}
String stringName = getMyComponents(name);
try {
registry.unbind(stringName);
} catch (NotBoundException e) {
// Returning ok if target name is not found, by the specification.
} catch (RemoteException e) {
throw (NamingException) newNamingException(e).fillInStackTrace();
}
}
/**
* {@inheritDoc}
*/
public void unbind(String name) throws NamingException {
unbind(new CompositeName(name));
}
/**
* {@inheritDoc}
*/
public Context createSubcontext(Name name)
throws OperationNotSupportedException {
// jndi.7F=RMI Registry is a flat context and doesn't support
// subcontexts
throw new OperationNotSupportedException(Messages.getString("jndi.7F")); //$NON-NLS-1$
}
/**
* {@inheritDoc}
*/
public Context createSubcontext(String name) throws NamingException {
return createSubcontext(new CompositeName(name));
}
/**
* {@inheritDoc}
*/
public void destroySubcontext(Name name)
throws OperationNotSupportedException {
// jndi.7F=RMI Registry is a flat context and doesn't support
// subcontexts
throw new OperationNotSupportedException(Messages.getString("jndi.7F")); //$NON-NLS-1$
}
/**
* {@inheritDoc}
*/
public void destroySubcontext(String name) throws NamingException {
destroySubcontext(new CompositeName(name));
}
/**
* {@inheritDoc}
*/
public void rename(Name oldName, Name newName) throws NamingException {
bind(newName, lookup(oldName));
unbind(oldName);
}
/**
* {@inheritDoc}
*/
public void rename(String oldName, String newName) throws NamingException {
rename(new CompositeName(oldName), new CompositeName(newName));
}
/**
* {@inheritDoc}
*/
public NamingEnumeration<NameClassPair> list(Name name)
throws NamingException {
if (name.isEmpty()) {
try {
return new NameClassPairEnumeration(registry.list());
} catch (RemoteException e) {
throw (NamingException) newNamingException(e)
.fillInStackTrace();
}
}
Object obj = lookup(name);
if (obj instanceof Context) {
try {
return ((Context) obj).list(""); //$NON-NLS-1$
} finally {
((Context) obj).close();
}
}
// jndi.80=Name specifies an object that is not a context: {0}
throw new NotContextException(Messages.getString("jndi.80", name)); //$NON-NLS-1$
}
/**
* {@inheritDoc}
*/
public NamingEnumeration<NameClassPair> list(String name)
throws NamingException {
return list(new CompositeName(name));
}
/**
* {@inheritDoc}
*/
public NamingEnumeration<Binding> listBindings(Name name)
throws NamingException {
if (name.isEmpty()) {
try {
return new BindingEnumeration(registry.list(), this);
} catch (RemoteException e) {
throw (NamingException) newNamingException(e)
.fillInStackTrace();
}
}
Object obj = lookup(name);
if (obj instanceof Context) {
try {
return ((Context) obj).listBindings(""); //$NON-NLS-1$
} finally {
((Context) obj).close();
}
}
// jndi.80=Name specifies an object that is not a context: {0}
throw new NotContextException(Messages.getString("jndi.80", name)); //$NON-NLS-1$
}
/**
* {@inheritDoc}
*/
public NamingEnumeration<Binding> listBindings(String name)
throws NamingException {
return listBindings(new CompositeName(name));
}
/**
* {@inheritDoc}
*/
public NameParser getNameParser(Name name) {
return nameParser;
}
/**
* {@inheritDoc}
*/
public NameParser getNameParser(String name) throws NamingException {
return getNameParser(new CompositeName(name));
}
/**
* {@inheritDoc}
*/
public Name composeName(Name name, Name prefix) throws NamingException {
return ((Name) prefix.clone()).addAll(name);
}
/**
* {@inheritDoc}
*/
public String composeName(String name, String prefix)
throws NamingException {
return composeName(new CompositeName(name), new CompositeName(prefix))
.toString();
}
/**
* {@inheritDoc}
*/
public String getNameInNamespace() {
return ""; //$NON-NLS-1$
}
/**
* {@inheritDoc}
*/
public Hashtable<?, ?> getEnvironment() {
return (Hashtable<?, ?>) environment.clone();
}
/**
* {@inheritDoc}
*/
public Object addToEnvironment(String propName, Object propVal)
throws NoPermissionException {
if (propName.equals(SECURITY_MANAGER)) {
installSecurityManager();
}
return environment.put(propName, propVal);
}
/**
* {@inheritDoc}
*/
public Object removeFromEnvironment(String propName) {
return environment.remove(propName);
}
/**
* {@inheritDoc}
*/
public void close() {
environment = null;
registry = null;
}
/**
* {@inheritDoc}
*/
public Reference getReference() throws NamingException {
if (reference == null) {
if ((host == null) || (host.equals("localhost"))) { //$NON-NLS-1$
// jndi.81=Cannot create reference for RMI registry that is
// being accessed using localhost
throw new ConfigurationException(Messages.getString("jndi.81")); //$NON-NLS-1$
}
reference = new Reference(RegistryContext.class.getName(),
new StringRefAddr(ADDRESS_TYPE, RMI_URL_PREFIX + "//" //$NON-NLS-1$
+ host + ((port > 0) ? (":" + port) : "")), //$NON-NLS-1$ //$NON-NLS-2$
RegistryContextFactory.class.getName(), null);
}
return (Reference) reference.clone();
}
/**
* Initializes reference for this context instance. Called by
* {@link RegistryContextFactory#getObjectInstance(Object, Name, Context, Hashtable)}.
*
* @param reference
* Reference for this context instance.
*/
protected void setReference(Reference reference) {
this.reference = reference;
}
/**
* Returns a clone of this context.
*
* @return Clone of this context.
*/
protected RegistryContext cloneContext() {
return new RegistryContext(this);
}
/**
* Verifies that the specified name is valid for this context and returns
* string representation of that name for this provider.
*
* Returns returns first component of a {@link CompositeName} or a string
* representation of a name otherwise.
*
* @param name
* Name to verify.
*
* @return {@link CompositeName#get(int) CompositeName#get(0)} if
* <code>name</code> is a {@link CompositeName},
* {@link Object#toString() name.toString()} otherwise.
*/
protected String getMyComponents(Name name) {
if (name instanceof CompositeName) {
return name.get(0);
}
return name.toString();
}
/**
* Prepares object for binding. It calls
* {@link NamingManager#getStateToBind(Object, Name, Context, Hashtable)}
* and makes the resulting object {@link Remote} by wrapping it into
* {@link RemoteReferenceWrapper}.
*
* @param name
* Object name.
*
* @param obj
* Object to prepare for binding.
*
* @return Object ready for binding.
*
* @throws NamingException
* If some naming error occurs.
*
* @throws RemoteException
* If remote exception occurs.
*/
protected Remote getStateToBind(String name, Object obj)
throws NamingException, RemoteException {
obj = NamingManager.getStateToBind(obj, new CompositeName().add(name),
this, environment);
if (obj instanceof Remote) {
return (Remote) obj;
}
if (obj instanceof Reference) {
return new RemoteReferenceWrapper((Reference) obj);
}
if (obj instanceof Referenceable) {
return new RemoteReferenceWrapper(((Referenceable) obj)
.getReference());
}
// jndi.82=Cannot bind to RMI Registry object that is neither Remote nor
// Reference nor Referenceable
throw new IllegalArgumentException(Messages.getString("jndi.82")); //$NON-NLS-1$
}
/**
* Processes object returned from {@linkplain Registry RMI registry}. It
* unwraps {@link RemoteReference} if necessary and calls
* {@link NamingManager#getObjectInstance(Object, Name, Context, Hashtable)}.
*
* @param name
* Object name.
*
* @param remote
* Returned object.
*
* @return Processed object.
*
* @throws NamingException
* If some naming error occurs.
*
* @throws RemoteException
* If remote exception occurs.
*/
protected Object getObjectInstance(String name, Remote remote)
throws NamingException, RemoteException {
Object obj;
obj = ((remote instanceof RemoteReference) ? ((RemoteReference) remote)
.getReference() : (Object) remote);
try {
return NamingManager.getObjectInstance(obj, new CompositeName()
.add(name), this, environment);
} catch (NamingException e) {
throw e;
} catch (RemoteException e) {
throw e;
} catch (Exception e) {
// jndi.83=NamingManager.getObjectInstance() failed
throw (NamingException) new NamingException(Messages
.getString("jndi.83")).initCause(e); //$NON-NLS-1$
}
}
/**
* Prepares a new {@link NamingException} wrapping the specified
* {@link RemoteException} source exception. Source exception becomes a
* {@linkplain NamingException#getCause() cause} of the generated exception.
*
* The particular subclass of {@link NamingException} returned depends on
* the particular subclass of source {@link RemoteException}.
*
* If source exception is not of a specific class or is not a
* {@link RemoteException} or is <code>null</code>, then plain
* {@link NamingException} is returned.
*
* Note: {@link Throwable#fillInStackTrace()} should be called before
* throwing the generated exception, to provide the proper (not including
* this method) stack trace for the exception.
*
* Example of use:
*
* <code>try {
* ...
* } catch (RemoteException e) {
* throw (NamingException) newNamingException(e).fillInStackTrace();
* }</code>
*
* @param e
* Source {@link RemoteException}.
*
* @return Generated {@link NamingException} exception.
*/
@SuppressWarnings("deprecation")
protected NamingException newNamingException(Throwable e) {
NamingException ret = (e instanceof AccessException) ? new NoPermissionException()
: (e instanceof ConnectException) ? new ServiceUnavailableException()
: (e instanceof ConnectIOException)
|| (e instanceof ExportException)
|| (e instanceof MarshalException)
|| (e instanceof UnmarshalException) ? new CommunicationException()
: (e instanceof ActivateFailedException)
|| (e instanceof NoSuchObjectException)
|| (e instanceof java.rmi.server.SkeletonMismatchException)
|| (e instanceof java.rmi.server.SkeletonNotFoundException)
|| (e instanceof StubNotFoundException)
|| (e instanceof UnknownHostException) ? new ConfigurationException()
: (e instanceof ServerException) ? newNamingException(e
.getCause())
: new NamingException();
if (ret.getCause() == null) {
ret.initCause(e);
}
return ret;
}
/**
* Installs {@link RMISecurityManager} if it is not already installed.
*
* @throws NoPermissionException
* If security manager other than {@link RMISecurityManager} is
* installed and prohibits installing a new security manager.
*/
protected void installSecurityManager() throws NoPermissionException {
if (!(System.getSecurityManager() instanceof RMISecurityManager)) {
try {
System.setSecurityManager(new RMISecurityManager());
} catch (SecurityException e) {
// jndi.84=Cannot install RMISecurityManager
throw (NoPermissionException) new NoPermissionException(
Messages.getString("jndi.84")).initCause(e); //$NON-NLS-1$
}
}
}
/**
* Creates reference to the {@linkplain Registry RMI registry} located on
* the specified host and port.
*
* @param host
* Host. If <code>null</code>, localhost is assumed. May not
* be <code>null</code> if <code>csf</code> is not
* <code>null</code>.
*
* @param port
* Port. If <code>0</code>, default registry port (<code>1099</code>)
* is assumed. May not be <code>0</code> if <code>csf</code>
* is not <code>null</code>.
*
* @param csf
* RMIClientSocketFactory that is used to create socket
* connections to the registry. If <code>null</code>, default
* socket factory is used. See
* {@link LocateRegistry#getRegistry(String, int, RMIClientSocketFactory)}.
*
* @return Registry reference.
*
* @throws NamingException
* If getting registry failed.
*/
protected Registry getRegistry(String host, int port,
RMIClientSocketFactory csf) throws NamingException {
try {
return ((csf != null) ? LocateRegistry.getRegistry(host, port, csf)
: ((host != null) ? ((port != 0) ? LocateRegistry
.getRegistry(host, port) : LocateRegistry
.getRegistry(host)) : ((port != 0) ? LocateRegistry
.getRegistry(port) : LocateRegistry.getRegistry())));
} catch (RemoteException e) {
throw (NamingException) new NamingException().initCause(e);
}
}
}