Package net.jini.jeri

Source Code of net.jini.jeri.AbstractILFactory

/*
* 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 net.jini.jeri;

import com.sun.jini.jeri.internal.runtime.Util;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.server.ExportException;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.jini.core.constraint.RemoteMethodControl;
import net.jini.export.ExportPermission;
import net.jini.security.Security;

/**
* An abstract implementation of {@link InvocationLayerFactory} that
* provides a convenient way for subclasses to create proxies and
* invocation dispatchers for remote objects.  A subclass must provide an
* implementation for at least the {@link #createInvocationHandler
* createInvocationHandler} and {@link #createInvocationDispatcher
* createInvocationDispatcher} methods.  A subclass can override the
* {@link #getProxyInterfaces getProxyInterfaces} method if its proxies
* need to implement a different set of interfaces than the default
* set (all remote interfaces of the remote object).
*
* @author  Sun Microsystems, Inc.
* @since 2.0
**/
public abstract class AbstractILFactory implements InvocationLayerFactory {

    /** Cached getClassLoader permission */
    private static final Permission getClassLoaderPermission =
  new RuntimePermission("getClassLoader");

    /** The class loader to define proxy classes in */
    private final ClassLoader loader;
   
    /**
     * Constructs an <code>AbstractILFactory</code> instance with a
     * <code>null</code> class loader.
     **/
    protected AbstractILFactory() {
  this(null);
    }

    /**
     * Constructs an <code>AbstractILFactory</code> instance with the
     * specified class loader.  The {@link #createInstances
     * createInstances} method uses the specified loader to define a proxy
     * class.
     *
     * @param  loader the class loader, or <code>null</code>
     **/
    protected AbstractILFactory(ClassLoader loader) {
  this.loader = loader;
    }

    /**
     * Returns a concatenated array of the interfaces in <code>i1</code>
     * followed by the interfaces in <code>i2</code> with duplicate
     * interfaces (that is, duplicates after the first occurence of an
     * interface) eliminated.
     *
     * @param  i1 an array of interfaces
     * @param  i2 an array of interfaces
     * @return   a concatenated array of the interfaces with duplicates
     *    eliminated
     * @throws  NullPointerException if any element in <code>i1</code> or
     *    <code>i2</code> is null
     **/
    private static Class[] combineInterfaces(Class[] i1, Class[] i2) {
  ArrayList list = new ArrayList(i1.length + i2.length);
  addInterfaces(list, i1);
  addInterfaces(list, i2);
  return (Class[]) list.toArray(new Class[list.size()]);
    }

    /**
     * Adds non-duplicate interfaces in the array to the list.
     *
     * @throws  NullPointerException if the interfaces array contains a
     *    <code>null</code> element
     **/
    private static void addInterfaces(ArrayList list, Class[] interfaces) {
  for (int i = 0; i < interfaces.length; i++) {
      Class cl = interfaces[i];
      if (cl == null) {
    throw new NullPointerException("array contains null element");
      }
      if (!list.contains(cl)) {
    list.add(cl);
      }
  }
    }

    /**
     * Returns the class loader specified during construction.
     * @return the class loader
     */
    protected final ClassLoader getClassLoader() {
  return loader;
    }
   
    /**
     * Returns a new array containing the interfaces for the proxy to
     * implement.
     *
     * <p><code>AbstractILFactory</code> implements this method to return
     * an array containing all of the interfaces obtained by passing
     * <code>impl</code> to the {@link #getRemoteInterfaces
     * getRemoteInterfaces} method plus the interfaces obtained by calling
     * the {@link #getExtraProxyInterfaces getExtraProxyInterfaces} method,
     * in that order, with duplicate interfaces (that is, duplicates after
     * the first occurrence of an interface) eliminated.
     *
     * <p>A subclass can override this method if its proxies need to
     * implement a different set of interfaces than the default.
     *
     * @param  impl the remote object
     * @return  the proxy interfaces
     * @throws  ExportException if there is a problem obtaining the
     *    proxy interfaces or if <code>impl</code> does not satisfy
     *    the requirements of this factory
     * @throws  NullPointerException if <code>impl</code> is <code>null</code>
     **/
    protected Class[] getProxyInterfaces(Remote impl)
  throws ExportException
    {
  return combineInterfaces(getRemoteInterfaces(impl),
         getExtraProxyInterfaces(impl));
    }
   
    /**
     * Returns a new array containing the remote interfaces that should be
     * implemented by the proxy.  All of the methods of a remote interface
     * are implemented as remote invocations, so all of the methods must
     * declare {@link java.rmi.RemoteException} or one of its superclasses
     * in their <code>throws</code> clauses.
     *
     * <p><code>AbstractILFactory</code> implements this method to return
     * an array containing the following ordered list of interfaces:
     *
     * <ul><li>for each superclass of <code>impl's</code> class starting
     * with <code>java.lang.Object</code> and following with each direct
     * subclass to the direct superclass of <code>impl</code>'s class, all
     * of the direct superinterfaces of the given superclass that extend
     * {@link Remote} and that do not appear previously in the list, in
     * declaration order (the order in which they are declared in the
     * class's <code>implements</code> clause), followed by
     *
     * <li>all of the direct superinterfaces of <code>impl</code>'s
     * class that extend {@link Remote} and that do not appear previously
     * in the list, in declaration order.
     * </ul>
     *
     * and throws an <code>ExportException</code> if any method of those
     * interfaces does not have a conforming <code>throws</code> clause.
     *
     * <p>A subclass can override this method if its proxies need a
     * set of remote interfaces other than the default.
     *
     * @param  impl the remote object
     * @return  the remote interfaces implemented by <code>impl</code>
     * @throws  NullPointerException if <code>impl</code> is <code>null</code>
     * @throws  ExportException if there is a problem obtaining the remote
     *     interfaces or if <code>impl</code> does not satisfy the
     *    requirements of this factory
     **/
    protected Class[] getRemoteInterfaces(Remote impl) throws ExportException {
  if (impl == null) {
      throw new NullPointerException("impl is null");
  }
  try {
      return Util.getRemoteInterfaces(impl.getClass());
  } catch (IllegalArgumentException e) {
      throw new ExportException("cannot get proxy interfaces", e);
  }
    }
   
    /**
     * Returns a new array containing any additional interfaces that the
     * proxy should implement, beyond the interfaces obtained by passing
     * <code>impl</code> to the {@link #getRemoteInterfaces
     * getRemoteInterfaces} method.
     *
     * <p><code>AbstractILFactory</code> implements this method to return
     * an array containing the {@link RemoteMethodControl} interface.
     *
     * <p>A subclass can override this method if its proxies need
     * to implement a different set of extra interfaces than the default.
     *
     * @param  impl the remote object
     * @return  the extra proxy interfaces
     * @throws  NullPointerException if <code>impl</code> is <code>null</code>
     * @throws  ExportException if there is a problem obtaining the additional
     *    interfaces or if <code>impl</code> does not satisfy the
     *    requirements of this factory
     **/
    protected Class[] getExtraProxyInterfaces(Remote impl)
  throws ExportException
    {
  if (impl == null) {
      throw new NullPointerException("impl is null");
  }
  return new Class[]{RemoteMethodControl.class};
    }

    /**
     * Returns a new, modifiable collection of {@link Method} objects,
     * containing all remote methods for which the invocation
     * dispatcher should accept incoming remote calls.
     *
     * <p><code>AbstractILFactory</code> implements this method to return a
     * {@link Set} containing all of the methods of the interfaces obtained
     * by passing <code>impl</code> to the {@link #getRemoteInterfaces
     * getRemoteInterfaces} method and satisfying the following
     * requirements:
     *
     * <ul>
     * <li>No duplicate methods with the same signature and return
     * type are contained in the set returned.  If the interfaces contain
     * more than one method with the same signature and return type, the
     * method in the returned collection will be a member of the foremost
     * of the interfaces that contains a method with that signature and
     * return type.
     *
     * <li>For each interface, if a security manager exists, its
     * <code>checkPackageAccess</code> method is invoked with the package
     * name of the interface; this invocation may throw a
     * <code>SecurityException</code>.
     *
     * <li>For each interface, if the interface is non-public and a
     * security manager exists, the security manager's
     * <code>checkPermission</code> method is invoked with the permission
     * (@link ExportPermission} constructed with the string
     * "exportRemoteInterface." concatenated with the fully qualified
     * interface name; this invocation may throw a
     * <code>SecurityException</code>.  If the security check passes, each
     * <code>Method</code> object of the non-public interface has its
     * accessibility flag set to suppress language access checks.
     * </ul>
     *
     * <p>A subclass can override this method if it needs to control the
     * selection of the set of methods for the dispatcher to handle, or if
     * it needs to control the implementation of the collection returned.
     *
     * @param  impl the remote object
     * @return  the remote methods
     * @throws  NullPointerException if <code>impl</code> is <code>null</code>
     * @throws  ExportException if there is a problem obtaining the remote
     *    methods or if <code>impl</code> does not satisfy the
     *    requirements of this factory
     **/
    protected Collection getInvocationDispatcherMethods(Remote impl)
  throws ExportException
    {
  Class[] interfaces = getRemoteInterfaces(impl);
  Map methodMap = new HashMap();

  for (int i = interfaces.length-1; i >= 0; i--) {
      Class intf = interfaces[i];
      boolean nonpublic = checkNonPublicInterface(intf);
      Util.checkPackageAccess(intf);
      Method interfaceMethods[] = intf.getMethods();
      for (int j = interfaceMethods.length-1; j >=0; j--) {
    final Method m = interfaceMethods[j];
    /*
     * If the interface is non-public, set this Method object
     * to override language access checks so that the
     * dispatcher can invoke methods from non-public remote
     * interfaces.  This is okay because the
     * checkNonPublicInterface method checked a permission for
     * this action.
     */
    if (nonpublic) {
        Security.doPrivileged(new PrivilegedAction() {
      public Object run() {
          m.setAccessible(true);
          return null;
      }
        });
    }
    methodMap.put(Util.getMethodNameAndDescriptor(m), m);
      }
  }
 
  Collection methods = new HashSet();
  methods.addAll(methodMap.values());
  return methods;
    }

    /**
     * Returns <code>true</code> if and only if the specified interface is
     * non-public.
     *
     * <p>If the interface is non-public and a security manager exists, the
     * security manager's <code>checkPermission</code> method is invoked
     * with the permission (@link ExportPermission} constructed with the
     * string "exportRemoteInterface." concatenated with the fully
     * qualified interface name; this invocation may throw a
     * <code>SecurityException</code>.
     *
     * @return <code>true</code> if the specified interface is non-public;
     *     otherwise returns <code>false</code>
     * @throws  SecurityException if the specified interface is non-public
     *    and the security check fails
     **/
    private static boolean checkNonPublicInterface(Class intf) {
  if (!Modifier.isPublic(intf.getModifiers())) {
      SecurityManager sm = System.getSecurityManager();
      if (sm != null) {
    String pString = "exportRemoteInterface." + intf.getName();
    sm.checkPermission(new ExportPermission(pString));
      }
      return true;
  }
  return false;
    }
   
   /**
     * Returns an invocation handler to use with a {@link Proxy} instance
     * implementing the specified interfaces, communicating with the
     * specified remote object using the specified object endpoint.
     *
     * <p>A subclass must override this method to create an
     * <code>InvocationHandler</code> for the specified interfaces, remote
     * object, and object endpoint.
     *
     * @param  interfaces an array of proxy interfaces
     * @param  impl a remote object this invocation handler
     *    is being created for
     * @param  oe an object endpoint used to communicate with
     *    the remote object
     * @return  the invocation handler for the remote object's proxy
     * @throws  ExportException if there is a problem creating the
     *     invocation handler
     * @throws  NullPointerException if any argument is <code>null</code>,
     *    or if <code>interfaces</code> contains a <code>null</code>
     *    element
     **/
    protected abstract InvocationHandler
  createInvocationHandler(Class[] interfaces,
        Remote impl,
        ObjectEndpoint oe)
  throws ExportException;

    /**
     * Returns an invocation dispatcher to receive incoming remote calls
     * for the specified methods to the specified remote object, for a
     * server and transport with the specified capabilities.
     *
     * <p>A subclass must override this method to create an
     * <code>InvocationDispatcher</code> for the specified methods, remote
     * object, and server capabilities.
     *
     * @param  methods a collection of {@link Method} instances for the
     *    remote methods
     * @param  impl a remote object that the dispatcher is being created for
     * @param  caps the transport capabilities of the server
     * @return  the invocation dispatcher for the remote object
     * @throws  ExportException if there is a problem creating the
     *    dispatcher
     * @throws  IllegalArgumentException if <code>methods</code> contains
     *    an element that is not a <code>Method</code> instance
     * @throws  NullPointerException if any argument is <code>null</code>,
     *    or if <code>methods</code> contains a <code>null</code> element
     **/
    protected abstract InvocationDispatcher
  createInvocationDispatcher(Collection methods,
           Remote impl,
           ServerCapabilities caps)
  throws ExportException;

    /**
     * {@inheritDoc}
     *
     * <p><code>AbstractILFactory</code> implements this method to return a
     * {@link Proxy} instance where:
     * <ul>
     *
     * <li>If the class loader specified during construction is not
     * <code>null</code>, the proxy's class is defined by the specified
     * loader.  Otherwise, if a security manager exists, its {@link
     * SecurityManager#checkPermission checkPermission} method is invoked
     * with the <code>{@link RuntimePermission}("getClassLoader")</code>
     * permission; this invocation may throw a
     * <code>SecurityException</code>.  If the above security check
     * succeeds, the proxy's class is defined by the class loader of
     * <code>impl</code>'s class.
     *
     * <li>The proxy implements the set of interfaces obtained by calling
     * the {@link #getProxyInterfaces getProxyInterfaces} method, passing
     * <code>impl</code> as the argument.  If a security manager exists,
     * for each interface returned from the <code>getProxyInterfaces</code>
     * method,  the security manager's {@link
     * SecurityManager#checkPackageAccess checkPackageAccess} method is
     * invoked with the package name of the interface. Such an invocation
     * may throw a <code>SecurityException</code>.
     *
     * <li>The invocation handler of the proxy instance is obtained by calling
     * the {@link #createInvocationHandler createInvocationHandler} method,
     * passing the proxy interfaces (as above), <code>impl</code>, and
     * <code>oe</code> as arguments.
     * </ul>
     *
     * <p>The returned invocation dispatcher is obtained by calling the
     * {@link #createInvocationDispatcher createInvocationDispatcher}
     * method, passing a collection of methods, <code>impl</code>, and
     * <code>caps</code> as arguments. The collection of methods is
     * obtained by calling the {@link #getInvocationDispatcherMethods
     * getInvocationDispatcherMethods} method, passing <code>impl</code> as
     * the argument.
     *
     * @throws  NullPointerException {@inheritDoc}
     **/
    public Instances createInstances(Remote impl,
             ObjectEndpoint oe,
             ServerCapabilities caps)
      throws ExportException
    {
  if (impl == null || oe == null || caps == null) {
      throw new NullPointerException();
  }
   Class[] interfaces = getProxyInterfaces(impl);
  InvocationHandler handler =
      createInvocationHandler(interfaces, impl, oe);
 
  ClassLoader proxyLoader;
  if (loader != null) {
      proxyLoader = loader;
  } else {
      SecurityManager security = System.getSecurityManager();
      if (security != null) {
    security.checkPermission(getClassLoaderPermission);
      }
      proxyLoader = impl.getClass().getClassLoader();
  }

  for (int i = 0; i < interfaces.length; i++) {
      Util.checkPackageAccess(interfaces[i].getClass());
  }
 
  Remote proxy;
  try {
      proxy = (Remote) Proxy.newProxyInstance(proxyLoader,
                interfaces,
                handler);
  } catch (IllegalArgumentException e) {
      throw new ExportException("unable to create proxy", e);
  }
  InvocationDispatcher dispatcher =
      createInvocationDispatcher(getInvocationDispatcherMethods(impl),
               impl, caps);
  return new Instances(proxy, dispatcher);
    }
   
    /**
     * Returns a hash code value for this factory.
     **/
    public int hashCode() {
  return getClass().hashCode();
    }

    /**
     * Compares the specified object with this invocation layer factory for
     * equality.
     *
     * <p><code>AbstractILFactory</code> implements this method to return
     * <code>true</code> if and only if the specified object has the same
     * class as this object and the loader in the specified object is equal
     * to the loader in this object.
     *
     * <p>A subclass should override this method if it adds instance state
     * that affects equality.
     **/
    public boolean equals(Object obj) {
  return (obj == this ||
    (obj != null &&
     obj.getClass() == getClass() &&
     ((AbstractILFactory) obj).loader == loader));
    }

    /**
     * Returns a string representation for this factory.
     **/
    public String toString() {
  String name = getClass().getName();
  return name.substring(name.lastIndexOf('.') + 1);
    }
}
TOP

Related Classes of net.jini.jeri.AbstractILFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.