/*
* @(#)IxcRegistryImpl.java 1.25 05/05/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
package com.sun.xlet.ixc;
import javax.microedition.xlet.ixc.*;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.AlreadyBoundException;
import java.rmi.registry.Registry;
import javax.microedition.xlet.XletContext;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
/**
* <code>IXCRegistry</code> is the bootstrap mechanism for obtaining
* references to remote objects residing in other Xlets executing on
* the same machine, but in separate classloaders.
* * <p>
* Instances of <code>IXCRegistry</code> are never accessible via
* <code>java.rmi.Naming</code> or
* <code>java.rmi.registry.LocateRegistry</code> if RMI functionality
* is implemented.
*
* @see java.rmi.Registry
*/
public class IxcRegistryImpl extends IxcRegistry {
// Hashtable<XletContext, IxcRegistry>
static Hashtable registries = new Hashtable(11);
// Hashtable<Name, XletContext>, who exported what names.
static Hashtable xletContextMap = new Hashtable(11);
// Hashtable<Name, RemoteObject>, who exported what Remote objects.
static Hashtable remoteObjectMap = new Hashtable(11);
// Lock object to synchronize access to
// remoteObjectMap and xletContextMap.
// We don't want Name to be appearing in one but not in another.
static private Object lock = new Object();
/* A thread for Remote method invocation */
private static Worker worker;
/* Xlet which this IxcRegistry belongs to*/
private XletContext context;
/* IxcClassLoader for this xlet */
IxcClassLoader ixcClassLoader;
/* ImportRegistries for this xlet.
<target xlet's XletContext, ImportRegis> */
Hashtable importRegistries = new Hashtable(11);
AccessControlContext acc;
/**
* Creates a IxcRegistry instance.
*/
protected IxcRegistryImpl(XletContext ctxt) {
context = ctxt;
final ClassLoader finalLoader = ctxt.getClassLoader();
ixcClassLoader = (IxcClassLoader) AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
return new IxcClassLoader(finalLoader);
}
}
);
acc = AccessController.getContext();
synchronized(registries) {
if (worker == null) // first time caller!
worker = new Worker("Remote invocation thread for IxcRegistry");
}
}
/**
* Gets the IxcRegistry
*/
public static IxcRegistryImpl getIxcRegistryImpl(XletContext ctxt) {
IxcRegistryImpl regis = (IxcRegistryImpl) registries.get(ctxt);
if (regis == null) {
IxcRegistryImpl newRegis = new IxcRegistryImpl(ctxt);
registries.put(ctxt, newRegis);
regis = newRegis;
}
return regis;
}
/**
* Returns a reference, a stub, for the remote object associated
* with the specified <code>name</code>.
*
* @param name a URL-formatted name for the remote object
* @return a reference for a remote object
* @exception NotBoundException if name is not currently bound
* @exception RemoteException if registry could not be contacted
* @exception AccessException if this operation is not permitted (if
* originating from a non-local host, for example)
*/
public Remote lookup(String name)
throws StubException, NotBoundException {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(new IxcPermission(name, "lookup"));
/* if not in the global hash, forget it */
if (!xletContextMap.containsKey(name)) {
throw new NotBoundException();
}
/* find out who exported this name */
XletContext targetXlet = (XletContext) xletContextMap.get(name);
/* When the xlet who exported the object is found,
we can consult appropriate ImportRegistry to make a stub*/
if (targetXlet != null) {
try {
Remote origRemoteObj =
(Remote)remoteObjectMap.get(name);
/* Returns the object wrapped as a 'stub' in the context
of this method's caller */
return getImportRegistry(targetXlet).lookup(origRemoteObj);
} catch (StubException se) {
throw se;
} catch (RemoteException re) {
// shouldn't happen, but just in case...
throw new StubException("lookup failed", re);
} catch (InterruptedException ie) {
throw new StubException("Can't lookup", ie);
}
}
return null;
}
/**
* Binds the specified <code>name</code> to a remote object.
*
* @param name a URL-formatted name for the remote object
* @param obj a reference for the remote object (usually a stub)
* @exception AlreadyBoundException if name is already bound
* @exception MalformedURLException if the name is not an appropriately
* formatted URL
* @exception RemoteException if registry could not be contacted
* @exception AccessException if this operation is not permitted (if
* originating from a non-local host, for example)
*/
public void bind(String name, Remote obj)
throws StubException, AlreadyBoundException {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(new IxcPermission(name, "bind"));
synchronized (lock) {
if (remoteObjectMap.get(name) != null) {
throw new AlreadyBoundException();
}
try {
rebind(name, obj);
} catch (AccessException e) {
// can't happen, but just in case,
throw new AlreadyBoundException();
}
}
}
/**
* Destroys the binding for the specified name that is associated
* with a remote object.
*
* @param name a URL-formatted name associated with a remote object
* @exception NotBoundException if name is not currently bound
* @exception MalformedURLException if the name is not an appropriately
* formatted URL
* @exception RemoteException if registry could not be contacted
* @exception AccessException if this operation is not permitted (if
* originating from a non-local host, for example)
*/
public void unbind(String name)
throws NotBoundException, AccessException {
/* 6254044, Exception checking - there are three cases.
* 1) The name was never bound (binderCtxt == null)
* 2) The name was binded by the same xlet as the caller
* of this method. (binderCtxt == this.context)
* 3) The name was binded by a different xlet.
* (binderCtxt != this.context && binderCtxt != null)
* Permission check is needed for (1) and (3) but not for (2).
*/
XletContext binderCtxt = (XletContext) xletContextMap.get(name);
if (binderCtxt != context) {
// Either case (1) or (3), Security check first.
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new IxcPermission(name, "bind"));
}
// The caller has the right permission, so just throw
// exceptions based on the condition.
if (binderCtxt == null) { // case (1)
throw new NotBoundException("Object not bound");
} else { // case (3)
throw new AccessException("Cannot unbind objects bound by other xlets");
}
}
// Case (2), proceed.
synchronized (lock) {
remoteObjectMap.remove(name);
xletContextMap.remove(name);
}
}
/**
* Rebinds the specified name to a new remote object. Any existing
* binding for the name is replaced.
*
* @param name a URL-formatted name associated with the remote object
* @param obj new remote object to associate with the name
* @exception MalformedURLException if the name is not an appropriately
* formatted URL
* @exception RemoteException if registry could not be contacted
* @exception AccessException if this operation is not permitted (if
* originating from a non-local host, for example)
*/
public void rebind(String name, Remote obj)
throws StubException, AccessException {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(new IxcPermission(name, "bind"));
/* first check if the remote interface is valid */
try {
ixcClassLoader.getStubClass(ixcClassLoader, obj.getClass());
} catch (RemoteException e) {
if (e instanceof StubException) throw (StubException) e;
else throw new StubException("Cannot bind", e);
}
/* validity check is done here */
synchronized (lock) {
Object ctxt = xletContextMap.get(name);
if (ctxt == null) {
xletContextMap.put(name, context);
} else if (ctxt != context) {
throw new AccessException("Cannot rebind an object of another xlet");
}
remoteObjectMap.put(name, obj);
}
}
/**
* Returns an array of the names bound in the registry. The names are
* URL-formatted strings. The array contains a snapshot of the names
* present in the registry at the time of the call.
*
* @return an array of names (in the appropriate URL format) bound
* in the registry
* @exception RemoteException if registry could not be contacted
* @exception AccessException if this operation is not permitted (if
* originating from a non-local host, for example)
*/
public String[] list() {
SecurityManager sm = System.getSecurityManager();
if (sm == null) {
synchronized (lock) {
return (String[]) remoteObjectMap.keySet().toArray(
new String[remoteObjectMap.size()]);
}
}
// SecurityManager is installed, return the names
// which the caller has the permission to lookup.
Vector v = new Vector();
synchronized (lock) {
String name;
for (Enumeration enumeration = remoteObjectMap.keys();
enumeration.hasMoreElements(); ) {
name = (String)enumeration.nextElement();
try {
sm.checkPermission(new IxcPermission(name, "lookup"));
v.add(name);
} catch (SecurityException e) {}
}
}
return (String[]) v.toArray(new String[v.size()]);
}
/**
* Removes the bindings for all remote objects currently exported by
* the calling Xlet.
*/
public void unbindAll() {
for (Enumeration e = xletContextMap.keys(); e.hasMoreElements();) {
String key = (String) e.nextElement();
XletContext other = (XletContext) xletContextMap.get(key);
if (other == context) {
// Cleanup xletContextMap and remoteObjectMap
synchronized(lock) {
xletContextMap.remove(key);
remoteObjectMap.remove(key);
}
} else {
// Clean up ImportRegistry for the other (alive) xlet
// which imported objects from this destroyed xlet
IxcRegistryImpl regis =
(IxcRegistryImpl) registries.get(other);
ImportRegistry ir = null;
synchronized (regis.importRegistries) {
ir = (ImportRegistry) regis.importRegistries.get(context);
if (ir != null) {
regis.importRegistries.remove(context);
}
}
if (ir != null) {
// This means that we have imported objects from other,
// so we need to unhook them.
ir.targetDestroyed();
}
}
}
// finally remove entry from IxcRegistry Hash itself
registries.remove(context);
return;
}
/**
* Returns an ImportRegistry of the target xlet which the xlet
* belonging to this IxcRegistry is trying to import an object
* from.
*/
ImportRegistry getImportRegistry(XletContext targetXlet) {
if (targetXlet == null)
return null;
ImportRegistry result;
synchronized (importRegistries) {
result = (ImportRegistry) importRegistries.get(targetXlet);
if (result == null) {
result = new ImportRegistry(context, targetXlet);
importRegistries.put(targetXlet, result);
}
}
return (ImportRegistry) result;
}
void executeForClient(Runnable r) throws RemoteException {
worker.execute(r);
}
// Convenience method to access IxcClassLoader based on XletContext
static IxcClassLoader getIxcClassLoader(XletContext ctxt) {
return getIxcRegistryImpl(ctxt).ixcClassLoader;
}
}