You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2014/05/09 09:03:19 UTC
svn commit: r1593493 [16/24] - in /river/jtsk/skunk/qa_refactor/trunk: qa/
qa/src/com/sun/jini/test/impl/end2end/jssewrapper/
qa/src/com/sun/jini/test/impl/joinmanager/
qa/src/com/sun/jini/test/impl/mahalo/
qa/src/com/sun/jini/test/impl/outrigger/match...
Modified: river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/start/ActivateWrapper.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/start/ActivateWrapper.java?rev=1593493&r1=1593492&r2=1593493&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/start/ActivateWrapper.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/start/ActivateWrapper.java Fri May 9 07:03:18 2014
@@ -1,665 +1,672 @@
-/*
- * 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 com.sun.jini.start;
-
-import org.apache.river.api.util.Startable;
-import net.jini.export.ProxyAccessor;
-import net.jini.id.Uuid;
-import net.jini.id.UuidFactory;
-import net.jini.loader.pref.PreferredClassLoader;
-import net.jini.security.policy.DynamicPolicy;
-import net.jini.security.policy.DynamicPolicyProvider;
-import net.jini.security.policy.PolicyFileProvider;
-
-import java.io.InvalidObjectException;
-import java.io.ObjectStreamException;
-import java.io.Serializable;
-import java.lang.reflect.Constructor;
-import java.net.URL;
-import java.rmi.MarshalException;
-import java.rmi.MarshalledObject;
-import java.rmi.Remote;
-import java.rmi.RemoteException;
-import java.rmi.activation.Activatable;
-import java.rmi.activation.ActivationDesc;
-import java.rmi.activation.ActivationException;
-import java.rmi.activation.ActivationGroupID;
-import java.rmi.activation.ActivationID;
-import java.rmi.activation.ActivationSystem;
-import java.security.AccessController;
-import java.security.AllPermission;
-import java.security.cert.Certificate;
-import java.security.CodeSource;
-import java.security.Permission;
-import java.security.Policy;
-import java.security.PrivilegedAction;
-import java.security.ProtectionDomain;
-import java.security.Security;
-import java.util.Arrays;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import net.jini.io.MarshalledInstance;
-import net.jini.loader.ClassLoading;
-
-/**
- * A wrapper for activatable objects, providing separation of the import
- * codebase (where the server classes are loaded from by the activation
- * group) from the export codebase (where clients should load classes from
- * for stubs, etc.) as well as providing an independent security policy file
- * for each activatable object. This functionality allows multiple
- * activatable objects to be placed in the same activation group, with each
- * object maintaining a distinct codebase and policy.
- * <p>
- * This wrapper class is assumed to be available directly in the activation
- * group VM; that is, it is assumed to be in the application classloader,
- * the extension classloader, or the boot classloader, rather than being
- * downloaded. Since this class also needs considerable permissions, the
- * easiest thing to do is to make it an installed extension.
- * <p>
- * This wrapper class performs a security check to control what
- * policy files can be used with a given codebase.
- * It does this by querying the VM's (global) policy for
- * {@link com.sun.jini.start.SharedActivationPolicyPermission}
- * grants. The service's associated
- * {@link com.sun.jini.start.ActivateWrapper.ActivateDesc#importLocation
- * ActivateDesc.importLocation} is used as
- * the {@link java.security.CodeSource}
- * for selecting the appropriate permission set to
- * check against. If multiple codebases are used, then all the codebases must
- * have the necessary <code>SharedActivationPolicyPermission</code> grants.
- * <p>
- * An example of how to use this wrapper:
- * <pre>
- * URL[] importURLs = new URL[] {new URL("http://myhost:8080/service.jar")};
- * URL[] exportURLs = new URL[] {new URL("http://myhost:8080/service-dl.jar")};
- * ActivationID aid
- * = ActivateWrapper.register(
- * gid,
- * new ActivateWrapper.ActivateDesc(
- * "foo.bar.ServiceImpl",
- * importURLs,
- * exportURLs,
- * "http://myhost:8080/service.policy",
- * new MarshalledObject(
- * new String[] { "/tmp/service.config" })
- * ),
- * true,
- * activationSystem);
- * </pre>
- * <A NAME="serviceConstructor">
- * Clients of this wrapper service need to implement the following "activation
- * constructor":
- * <blockquote><pre>
- * <impl>(ActivationID activationID, MarshalledObject data)
- * </blockquote></pre>
- * where,
- * <UL>
- * <LI>activationID - is the object's activation identifier
- * <LI>data - is the object's activation data
- * </UL>
- *
- * Clients of this wrapper service can also implement
- * {@link net.jini.export.ProxyAccessor}, which allows the service
- * implementation to provide a remote reference of its choosing.
- * <P>
- * <A NAME="configEntries">
- * This implementation of <code>ActivateWrapper</code>
- * supports the
- * following {@link java.security.Security} property:
- *
- * <table summary="Describes the com.sun.jini.start.servicePolicyProvider
- * security property"
- * border="0" cellpadding="2">
- * <tr valign="top">
- * <th scope="col" summary="layout"> <font size="+1">•</font>
- * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
- * com.sun.jini.start.servicePolicyProvider</code></font>
- * <tr valign="top"> <td>   <th scope="row" align="right">
- * Default: <td> <code>
- * "net.jini.security.policy.DynamicPolicyProvider"
- * </code>
- * <tr valign="top"> <td>   <th scope="row" align="right">
- * Description: <td> The fully qualified class name of a
- * dynamic policy provider (see {@link net.jini.security.policy.DynamicPolicy})
- * which will be used to "wrap" all service policy files.
- * The implementation class needs to:
- * <UL>
- * <LI> implement the following constructor:
- * <blockquote><pre>
- * public <impl>(Policy servicePolicy)
- * </blockquote></pre>
- * where,
- * <UL>
- * <LI>servicePolicy - is the service policy object to be wrapped
- * </UL>
- * <LI> implement {@link net.jini.security.policy.DynamicPolicy}
- * <LI> be a public, non-interface, non-abstract class
- * </UL>
- *
- * <P>
- * A custom service policy provider can be very useful when trying to
- * debug security related issues.
- * <code>com.sun.jini.tool.DebugDynamicPolicyProvider</code> is an example
- * policy provider that provides this functionality and can be located
- * via the following URL:
- * <A HREF="http://starterkit-examples.jini.org/">
- * http://starterkit-examples.jini.org/
- * </A><BR>
- * <I>Note:</I>The custom policy implementation is assumed to be
- * available from the system classloader of the virtual machine
- * hosting the service. Its codebase should also be granted
- * {@link java.security.AllPermission}.
- * </table>
- *
- * @see com.sun.jini.start.SharedActivationPolicyPermission
- * @see java.rmi.activation.ActivationID
- * @see java.rmi.MarshalledObject
- * @see java.rmi.Remote
- * @see java.security.CodeSource
- * @see net.jini.export.ProxyAccessor
- *
- * @author Sun Microsystems, Inc.
- *
- */
-
-public class ActivateWrapper implements Remote, Serializable {
-
- /** Configure logger */
- static final Logger logger = Logger.getLogger("com.sun.jini.start.wrapper");
- /**
- * The <code>Policy</code> object that aggregates the individual
- * service policy objects.
- */
- private static AggregatePolicyProvider globalPolicy;
-
- /**
- * The <code>Policy</code> object in effect at startup.
- */
- private static Policy initialGlobalPolicy;
-
- /**
- * The "wrapped" activatable object.
- * @serial
- */
- private final Object impl;
-
- /**
- * The parameter types for the "activation constructor".
- */
- private static final Class[] actTypes = {
- ActivationID.class, MarshalledObject.class
- };
-
- /**
- * Fully qualified name of custom, service policy provider
- */
- private static String servicePolicyProvider =
- ((String) AccessController.doPrivileged(
- new PrivilegedAction() {
- public Object run() {
- return Security.getProperty(
- "com.sun.jini.start." +
- "servicePolicyProvider");
- }
- }));
-
- /**
- * The parameter types for the
- * "custom, service policy constructor".
- */
- private static final Class[] policyTypes = {
- Policy.class
- };
-
- /**
- * Descriptor for registering a "wrapped" activatable object. This
- * descriptor gets stored as the <code>MarshalledObject</code>
- * initialization data in the <code>ActivationDesc</code>.
- */
- public static class ActivateDesc implements Serializable {
-
- private static final long serialVersionUID = 2L;
-
- /**
- * The activatable object's class name.
- * @serial
- */
- public final String className;
- /**
- * The codebase where the server classes are loaded from by the
- * activation group.
- * @serial
- */
- public final URL[] importLocation;
- /**
- * The codebase where clients should load classes from for stubs, etc.
- * @serial
- */
- public final URL[] exportLocation;
- /**
- * The security policy filename or URL.
- * @serial
- */
- public final String policy;
- /**
- * The activatable object's initialization data.
- * @serial
- */
- public final MarshalledObject data;
-
- /**
- * Trivial constructor.
- */
- public ActivateDesc(String className,
- URL[] importLocation,
- URL[] exportLocation,
- String policy,
- MarshalledObject data)
- {
-//TODO - clone non-String objects?
- this.className = className;
- this.importLocation = importLocation;
- this.exportLocation = exportLocation;
- this.policy = policy;
- this.data = data;
- }
- // Javadoc inherited from supertype
- public String toString() {
- return "[className=" + className + ","
- + "importLocation="
- + ((importLocation == null)
- ? null : Arrays.asList(importLocation))
- + ","
- + "exportLocation="
- + ((exportLocation == null)
- ? null : Arrays.asList(exportLocation))
- + ","
- + "policy=" + policy + ","
- + "data=" + data + "]";
- }
- }
-
- /**
- * A simple subclass of <code>PreferredClassLoader</code> that overrides
- * <code>getURLs</code> to
- * return the <code>URL</code>s of the provided export codebase.
- * <code>getURLs</code>
- * is called by the RMI subsystem in order to annotate objects
- * leaving the virtual machine.
- */
- /*
- * Implementation note. Subclasses of this class that override
- * getClassAnnotation might need to override getURLs because getURLs
- * uses a "cached" version of the export annotation.
- */
- static class ExportClassLoader extends PreferredClassLoader
- {
- /** Cached value of the provided export codebase <code>URL</code>s */
- private final URL[] exportURLs;
-
- /** Id field used to make toString() unique */
- private final Uuid id = UuidFactory.generate();
-
- /** Trivial constructor that calls
- * <pre>
- * super(importURLs, parent, urlsToPath(exportURLs), false);
- * </pre>
- * and assigns <code>exportURLs</code> to an internal field.
- */
- public ExportClassLoader(URL[] importURLs, URL[] exportURLs,
- ClassLoader parent)
- {
- super(importURLs, parent, urlsToPath(exportURLs), false);
- // Not safe to call getClassAnnotation() w/i cons if subclassed,
- // so need to redo "super" logic here.
- if (exportURLs == null) {
- this.exportURLs = importURLs;
- } else {
- this.exportURLs = exportURLs;
- }
- }
- //Javadoc inherited from super type
- public URL[] getURLs() {
- return (URL[])exportURLs.clone();
- }
-
- // Javadoc inherited from supertype
- public String toString() {
- URL[] urls = super.getURLs();
- return this.getClass().getName()
- + "[importURLs="
- + (urls==null?null:Arrays.asList(urls))
- + ","
- + "exportURLs="
- + (exportURLs==null?null:Arrays.asList(exportURLs))
- + ","
- + "parent=" + getParent()
- + ","
- + "id=" + id
- + "]";
- }
- }
-
- /**
- * Activatable constructor. This constructor:
- * <UL>
- * <LI>Retrieves an <code>ActivateDesc</code> from the
- * provided <code>data</code> parameter.
- * <LI>creates an <code>ExportClassLoader</code> using the
- * import and export codebases obtained from the provided
- * <code>ActivateDesc</code>,
- * <LI>checks the import codebase(s) for the required
- * <code>SharedActivationPolicyPermission</code>
- * <LI>associates the newly created <code>ExportClassLoader</code>
- * and the corresponding policy file obtained from the
- * <code>ActivateDesc</code> with the
- * <code>AggregatePolicyProvider</code>
- * <LI>loads the "wrapped" activatable object's class and
- * calls its activation constructor with the context classloader
- * set to the newly created <code>ExportClassLoader</code>.
- * <LI> resets the context class loader to the original
- * context classloader
- * </UL>
- * The first instance of this class will also replace the VM's
- * existing <code>Policy</code> object, if any,
- * with a <code>AggregatePolicyProvider</code>.
- *
- * @param id The <code>ActivationID</code> of this object
- * @param data The activation data for this object
- *
- * @see com.sun.jini.start.ActivateWrapper.ExportClassLoader
- * @see com.sun.jini.start.ActivateWrapper.ActivateDesc
- * @see com.sun.jini.start.AggregatePolicyProvider
- * @see com.sun.jini.start.SharedActivationPolicyPermission
- * @see java.security.Policy
- *
- */
- public ActivateWrapper(ActivationID id, MarshalledObject data)
- throws Exception
- {
- try {
- logger.entering(ActivateWrapper.class.getName(),
- "ActivateWrapper", new Object[] { id, data });
-
- ActivateDesc desc = (ActivateDesc)
- new MarshalledInstance(data).get(false);
- logger.log(Level.FINEST, "ActivateDesc: {0}", desc);
-
- Thread t = Thread.currentThread();
- ClassLoader ccl = t.getContextClassLoader();
- logger.log(Level.FINEST, "Saved current context class loader: {0}",
- ccl);
-
- ExportClassLoader cl = null;
- try {
- cl = new ExportClassLoader(desc.importLocation,
- desc.exportLocation,
- ccl);
- logger.log(Level.FINEST, "Created ExportClassLoader: {0}", cl);
- } catch (Exception e) {
- logger.throwing(ActivateWrapper.class.getName(),
- "ActivateWrapper", e);
- throw e;
- }
-
- checkPolicyPermission(desc.policy, desc.importLocation);
-
- synchronized (ActivateWrapper.class) {
- // supplant global policy 1st time through
- if (globalPolicy == null) {
- initialGlobalPolicy = Policy.getPolicy();
- if (!(initialGlobalPolicy instanceof DynamicPolicy)) {
- initialGlobalPolicy =
- new DynamicPolicyProvider(initialGlobalPolicy);
- }
- globalPolicy =
- new AggregatePolicyProvider(initialGlobalPolicy);
- Policy.setPolicy(globalPolicy);
- logger.log(Level.FINEST,
- "Global policy set: {0}", globalPolicy);
- }
- Policy service_policy =
- getServicePolicyProvider(
- new PolicyFileProvider(desc.policy));
- Policy backstop_policy =
- getServicePolicyProvider(initialGlobalPolicy);
- LoaderSplitPolicyProvider split_service_policy =
- new LoaderSplitPolicyProvider(
- cl, service_policy, backstop_policy);
- /* Grant "this" code enough permission to do its work
- * under the service policy, which takes effect (below)
- * after the context loader is (re)set.
- * Note: Throws UnsupportedOperationException if dynamic grants
- * aren't supported (because underlying policies don't support it).
- */
- split_service_policy.grant(
- this.getClass(),
- null, /* Principal[] */
- new Permission[] { new AllPermission() } );
- globalPolicy.setPolicy(cl, split_service_policy);
- logger.log(Level.FINEST,
- "Added policy to set: {0}", desc.policy);
- }
-
- boolean initialize = false;
- Class ac = ClassLoading.forName(desc.className, initialize, cl);
- logger.log(Level.FINEST, "Obtained implementation class: {0}", ac);
-
- t.setContextClassLoader(cl);
-
- try {
- logger.log(Level.FINEST,
- "Set new context class loader: {0}", cl);
- Constructor constructor =
- ac.getDeclaredConstructor(actTypes);
- logger.log(Level.FINEST,
- "Obtained implementation constructor: {0}",
- constructor);
- constructor.setAccessible(true);
- impl =
- constructor.newInstance(new Object[]{id, desc.data});
- if (impl instanceof Startable) {
- ((Startable) impl).start();
- } else {
- logger.log( Level.SEVERE,
- "Service {0} doesn''t implement {1} {2} {3} {4} {5} {6}",
- new Object []
- {
- impl.getClass().getCanonicalName(),
- Startable.class.getCanonicalName(),
- "this service is likely to suffer from race",
- "conditions caused by export performed during",
- "construction, or threads started while ''this''",
- "has been allowed to escape during construction",
- "https://www.securecoding.cert.org/confluence/display/java/TSM01-J.+Do+not+let+the+this+reference+escape+during+object+construction"
- }
- );
- }
- logger.log(Level.FINEST,
- "Obtained implementation instance: {0}", impl);
- } finally {
- t.setContextClassLoader(ccl);
- logger.log(Level.FINEST, "Context class loader reset to: {0}",
- ccl);
- }
- } catch (Exception e) {
- logger.throwing(ActivateWrapper.class.getName(),
- "ActivateWrapper", e);
- throw e;
- }
- logger.exiting(ActivateWrapper.class.getName(),
- "ActivateWrapper");
- }
-
-
- /**
- * Return a reference to service being wrapped in place
- * of this object.
- */
- private Object writeReplace() throws ObjectStreamException {
- Object impl_proxy = impl;
- if (impl instanceof ProxyAccessor) {
- impl_proxy = ((ProxyAccessor) impl).getProxy();
- logger.log(Level.FINEST,
- "Obtained implementation proxy: {0}", impl_proxy);
- if (impl_proxy == null) {
- throw new InvalidObjectException(
- "Implementation's getProxy() returned null");
- }
- }
- return impl_proxy;
- }
-
- /**
- * Analog to
- * {@link java.rmi.activation.Activatable#register(java.rmi.activation.ActivationDesc)
- * Activatable.register()} for activatable objects that want
- * to use this wrapper mechanism.
- *
- * @return activation ID of the registered service
- *
- * @throws ActivationException if there was a problem registering
- * the activatable class with the activation system
- * @throws RemoteException if there was a problem communicating
- * with the activation system
- */
- public static ActivationID register(ActivationGroupID gid,
- ActivateDesc desc,
- boolean restart,
- ActivationSystem sys)
- throws ActivationException, RemoteException
- {
- logger.entering(ActivateWrapper.class.getName(),
- "register", new Object[] { gid, desc, Boolean.valueOf(restart), sys });
-
- MarshalledObject data;
- try {
- data = new MarshalledInstance(desc).convertToMarshalledObject();
- } catch (Exception e) {
- MarshalException me =
- new MarshalException("marshalling ActivateDesc", e);
- logger.throwing(ActivateWrapper.class.getName(),
- "register", me);
- throw me;
- }
-
- ActivationDesc adesc =
- new ActivationDesc(gid,
- ActivateWrapper.class.getName(),
- null,
- data,
- restart
- );
- logger.log(Level.FINEST,
- "Registering descriptor with activation: {0}", adesc);
-
- ActivationID aid = sys.registerObject(adesc);
-
- logger.exiting(ActivateWrapper.class.getName(),
- "register", aid);
- return aid;
- }
-
- /**
- * Checks that all the provided <code>URL</code>s have permission to
- * use the given policy.
- */
- private static void checkPolicyPermission(String policy, URL[] urls) {
- logger.entering(ActivateWrapper.class.getName(),
- "checkPolicyPermission", new Object[] { policy, urlsToPath(urls) });
- // Create desired permission object
- Permission perm = new SharedActivationPolicyPermission(policy);
- Certificate[] certs = null;
- CodeSource cs = null;
- ProtectionDomain pd = null;
- // Loop over all codebases
- for (int i=0; i < urls.length; i++) {
- // Create ProtectionDomain for given codesource
- cs = new CodeSource(urls[i], certs);
- pd = new ProtectionDomain(cs, null, null, null);
- logger.log(Level.FINEST,
- "Checking protection domain: {0}", pd);
-
- // Check if current domain allows desired permission
- if(!pd.implies(perm)) {
- SecurityException se = new SecurityException(
- "ProtectionDomain " + pd
- + " does not have required permission: " + perm);
- logger.throwing(ActivateWrapper.class.getName(),
- "checkPolicyPermission", se);
- throw se;
- }
- }
- logger.exiting(ActivateWrapper.class.getName(),
- "checkPolicyPermission");
- }
-
- /**
- * Utility method that converts a <code>URL[]</code>
- * into a corresponding, space-separated string with
- * the same array elements.
- *
- * Note that if the array has zero elements, the return value is
- * null, not the empty string.
- */
- private static String urlsToPath(URL[] urls) {
-//TODO - check if spaces in file paths are properly escaped (i.e.% chars)
- if (urls == null) {
- return null;
- } else if (urls.length == 0) {
- return "";
- } else if (urls.length == 1) {
- return urls[0].toExternalForm();
- } else {
- StringBuffer path = new StringBuffer(urls[0].toExternalForm());
- for (int i = 1; i < urls.length; i++) {
- path.append(' ');
- path.append(urls[i].toExternalForm());
- }
- return path.toString();
- }
- }
-
- static Policy getServicePolicyProvider(Policy service_policy) throws Exception {
- Policy servicePolicyWrapper = null;
- if (servicePolicyProvider != null) {
- Class sp = Class.forName(servicePolicyProvider);
- logger.log(Level.FINEST,
- "Obtained custom service policy implementation class: {0}", sp);
- Constructor constructor =
- sp.getConstructor(policyTypes);
- logger.log(Level.FINEST,
- "Obtained custom service policy implementation constructor: {0}",
- constructor);
- servicePolicyWrapper = (Policy)
- constructor.newInstance(new Object[]{service_policy});
- logger.log(Level.FINEST,
- "Obtained custom service policy implementation instance: {0}",
- servicePolicyWrapper);
- } else {
- servicePolicyWrapper = new DynamicPolicyProvider(service_policy);
- logger.log(Level.FINEST,
- "Using default service policy implementation instance: {0}",
- servicePolicyWrapper);
- }
- return servicePolicyWrapper;
- }
-
-
-}
+/*
+ * 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 com.sun.jini.start;
+
+import org.apache.river.api.util.Startable;
+import net.jini.export.ProxyAccessor;
+import net.jini.id.Uuid;
+import net.jini.id.UuidFactory;
+import net.jini.loader.pref.PreferredClassLoader;
+import net.jini.security.policy.DynamicPolicy;
+import net.jini.security.policy.DynamicPolicyProvider;
+import net.jini.security.policy.PolicyFileProvider;
+
+import java.io.InvalidObjectException;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.net.URL;
+import java.rmi.MarshalException;
+import java.rmi.MarshalledObject;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.activation.ActivationDesc;
+import java.rmi.activation.ActivationException;
+import java.rmi.activation.ActivationGroupID;
+import java.rmi.activation.ActivationID;
+import java.rmi.activation.ActivationSystem;
+import java.security.AccessController;
+import java.security.AllPermission;
+import java.security.cert.Certificate;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.jini.io.MarshalledInstance;
+import net.jini.loader.ClassLoading;
+
+/**
+ * A wrapper for activatable objects, providing separation of the import
+ * codebase (where the server classes are loaded from by the activation
+ * group) from the export codebase (where clients should load classes from
+ * for stubs, etc.) as well as providing an independent security policy file
+ * for each activatable object. This functionality allows multiple
+ * activatable objects to be placed in the same activation group, with each
+ * object maintaining a distinct codebase and policy.
+ * <p>
+ * This wrapper class is assumed to be available directly in the activation
+ * group VM; that is, it is assumed to be in the application classloader,
+ * the extension classloader, or the boot classloader, rather than being
+ * downloaded. Since this class also needs considerable permissions, the
+ * easiest thing to do is to make it an installed extension.
+ * <p>
+ * This wrapper class performs a security check to control what
+ * policy files can be used with a given codebase.
+ * It does this by querying the VM's (global) policy for
+ * {@link com.sun.jini.start.SharedActivationPolicyPermission}
+ * grants. The service's associated
+ * {@link com.sun.jini.start.ActivateWrapper.ActivateDesc#importLocation
+ * ActivateDesc.importLocation} is used as
+ * the {@link java.security.CodeSource}
+ * for selecting the appropriate permission set to
+ * check against. If multiple codebases are used, then all the codebases must
+ * have the necessary <code>SharedActivationPolicyPermission</code> grants.
+ * <p>
+ * An example of how to use this wrapper:
+ * <pre>
+ * URL[] importURLs = new URL[] {new URL("http://myhost:8080/service.jar")};
+ * URL[] exportURLs = new URL[] {new URL("http://myhost:8080/service-dl.jar")};
+ * ActivationID aid
+ * = ActivateWrapper.register(
+ * gid,
+ * new ActivateWrapper.ActivateDesc(
+ * "foo.bar.ServiceImpl",
+ * importURLs,
+ * exportURLs,
+ * "http://myhost:8080/service.policy",
+ * new MarshalledObject(
+ * new String[] { "/tmp/service.config" })
+ * ),
+ * true,
+ * activationSystem);
+ * </pre>
+ * <A NAME="serviceConstructor">
+ * Clients of this wrapper service need to implement the following "activation
+ * constructor":
+ * <blockquote><pre>
+ * <impl>(ActivationID activationID, MarshalledObject data)
+ * </blockquote></pre>
+ * where,
+ * <UL>
+ * <LI>activationID - is the object's activation identifier
+ * <LI>data - is the object's activation data
+ * </UL>
+ *
+ * Clients of this wrapper service can also implement
+ * {@link net.jini.export.ProxyAccessor}, which allows the service
+ * implementation to provide a remote reference of its choosing.
+ * <P>
+ * <A NAME="configEntries">
+ * This implementation of <code>ActivateWrapper</code>
+ * supports the
+ * following {@link java.security.Security} property:
+ *
+ * <table summary="Describes the com.sun.jini.start.servicePolicyProvider
+ * security property"
+ * border="0" cellpadding="2">
+ * <tr valign="top">
+ * <th scope="col" summary="layout"> <font size="+1">•</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * com.sun.jini.start.servicePolicyProvider</code></font>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Default: <td> <code>
+ * "net.jini.security.policy.DynamicPolicyProvider"
+ * </code>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Description: <td> The fully qualified class name of a
+ * dynamic policy provider (see {@link net.jini.security.policy.DynamicPolicy})
+ * which will be used to "wrap" all service policy files.
+ * The implementation class needs to:
+ * <UL>
+ * <LI> implement the following constructor:
+ * <blockquote><pre>
+ * public <impl>(Policy servicePolicy)
+ * </blockquote></pre>
+ * where,
+ * <UL>
+ * <LI>servicePolicy - is the service policy object to be wrapped
+ * </UL>
+ * <LI> implement {@link net.jini.security.policy.DynamicPolicy}
+ * <LI> be a public, non-interface, non-abstract class
+ * </UL>
+ *
+ * <P>
+ * A custom service policy provider can be very useful when trying to
+ * debug security related issues.
+ * <code>com.sun.jini.tool.DebugDynamicPolicyProvider</code> is an example
+ * policy provider that provides this functionality and can be located
+ * via the following URL:
+ * <A HREF="http://starterkit-examples.jini.org/">
+ * http://starterkit-examples.jini.org/
+ * </A><BR>
+ * <I>Note:</I>The custom policy implementation is assumed to be
+ * available from the system classloader of the virtual machine
+ * hosting the service. Its codebase should also be granted
+ * {@link java.security.AllPermission}.
+ * </table>
+ *
+ * @see com.sun.jini.start.SharedActivationPolicyPermission
+ * @see java.rmi.activation.ActivationID
+ * @see java.rmi.MarshalledObject
+ * @see java.rmi.Remote
+ * @see java.security.CodeSource
+ * @see net.jini.export.ProxyAccessor
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ */
+
+public class ActivateWrapper implements Remote, Serializable {
+
+ /** Configure logger */
+ static final Logger logger = Logger.getLogger("com.sun.jini.start.wrapper");
+ /**
+ * The <code>Policy</code> object that aggregates the individual
+ * service policy objects.
+ */
+ private static AggregatePolicyProvider globalPolicy;
+
+ /**
+ * The <code>Policy</code> object in effect at startup.
+ */
+ private static Policy initialGlobalPolicy;
+
+ /**
+ * The "wrapped" activatable object.
+ * @serial
+ */
+ private final Object impl;
+
+ /**
+ * The parameter types for the "activation constructor".
+ */
+ private static final Class[] actTypes = {
+ ActivationID.class, MarshalledObject.class
+ };
+
+ /**
+ * Fully qualified name of custom, service policy provider
+ */
+ private static final String servicePolicyProvider =
+ ((String) AccessController.doPrivileged(
+ new PrivilegedAction() {
+ @Override
+ public Object run() {
+ return Security.getProperty(
+ "com.sun.jini.start." +
+ "servicePolicyProvider");
+ }
+ }));
+
+ /**
+ * The parameter types for the
+ * "custom, service policy constructor".
+ */
+ private static final Class[] policyTypes = {
+ Policy.class
+ };
+
+ /**
+ * Descriptor for registering a "wrapped" activatable object. This
+ * descriptor gets stored as the <code>MarshalledObject</code>
+ * initialization data in the <code>ActivationDesc</code>.
+ */
+ public static class ActivateDesc implements Serializable {
+
+ private static final long serialVersionUID = 2L;
+
+ /**
+ * The activatable object's class name.
+ * @serial
+ */
+ public final String className;
+ /**
+ * The codebase where the server classes are loaded from by the
+ * activation group.
+ * @serial
+ */
+ public final URL[] importLocation;
+ /**
+ * The codebase where clients should load classes from for stubs, etc.
+ * @serial
+ */
+ public final URL[] exportLocation;
+ /**
+ * The security policy filename or URL.
+ * @serial
+ */
+ public final String policy;
+ /**
+ * The activatable object's initialization data.
+ * @serial
+ */
+ public final MarshalledObject data;
+
+ /**
+ * Trivial constructor.
+ */
+ public ActivateDesc(String className,
+ URL[] importLocation,
+ URL[] exportLocation,
+ String policy,
+ MarshalledObject data)
+ {
+//TODO - clone non-String objects?
+ this.className = className;
+ this.importLocation = importLocation;
+ this.exportLocation = exportLocation;
+ this.policy = policy;
+ this.data = data;
+ }
+ // Javadoc inherited from supertype
+ @Override
+ public String toString() {
+ return "[className=" + className + ","
+ + "importLocation="
+ + ((importLocation == null)
+ ? null : Arrays.asList(importLocation))
+ + ","
+ + "exportLocation="
+ + ((exportLocation == null)
+ ? null : Arrays.asList(exportLocation))
+ + ","
+ + "policy=" + policy + ","
+ + "data=" + data + "]";
+ }
+ }
+
+ /**
+ * A simple subclass of <code>PreferredClassLoader</code> that overrides
+ * <code>getURLs</code> to
+ * return the <code>URL</code>s of the provided export codebase.
+ * <code>getURLs</code>
+ * is called by the RMI subsystem in order to annotate objects
+ * leaving the virtual machine.
+ */
+ /*
+ * Implementation note. Subclasses of this class that override
+ * getClassAnnotation might need to override getURLs because getURLs
+ * uses a "cached" version of the export annotation.
+ */
+ static class ExportClassLoader extends PreferredClassLoader
+ {
+ /** Cached value of the provided export codebase <code>URL</code>s */
+ private final URL[] exportURLs;
+
+ /** Id field used to make toString() unique */
+ private final Uuid id = UuidFactory.generate();
+
+ /** Trivial constructor that calls
+ * <pre>
+ * super(importURLs, parent, urlsToPath(exportURLs), false);
+ * </pre>
+ * and assigns <code>exportURLs</code> to an internal field.
+ */
+ public ExportClassLoader(URL[] importURLs, URL[] exportURLs,
+ ClassLoader parent)
+ {
+ super(importURLs, parent, urlsToPath(exportURLs), false);
+ // Not safe to call getClassAnnotation() w/i cons if subclassed,
+ // so need to redo "super" logic here.
+ if (exportURLs == null) {
+ this.exportURLs = importURLs;
+ } else {
+ this.exportURLs = exportURLs;
+ }
+ }
+ //Javadoc inherited from super type
+ @Override
+ public URL[] getURLs() {
+ return (URL[])exportURLs.clone();
+ }
+
+ // Javadoc inherited from supertype
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(200);
+ URL[] urls = super.getURLs();
+ return builder
+ .append(this.getClass().getName())
+ .append( "[importURLs=" )
+ .append(urls==null?null:Arrays.asList(urls))
+ .append(",")
+ .append("exportURLs=")
+ .append(exportURLs==null?null:Arrays.asList(exportURLs))
+ .append(",")
+ .append("parent=").append(getParent())
+ .append(",")
+ .append("id=").append(id)
+ .append("]").toString();
+ }
+ }
+
+ /**
+ * Activatable constructor. This constructor:
+ * <UL>
+ * <LI>Retrieves an <code>ActivateDesc</code> from the
+ * provided <code>data</code> parameter.
+ * <LI>creates an <code>ExportClassLoader</code> using the
+ * import and export codebases obtained from the provided
+ * <code>ActivateDesc</code>,
+ * <LI>checks the import codebase(s) for the required
+ * <code>SharedActivationPolicyPermission</code>
+ * <LI>associates the newly created <code>ExportClassLoader</code>
+ * and the corresponding policy file obtained from the
+ * <code>ActivateDesc</code> with the
+ * <code>AggregatePolicyProvider</code>
+ * <LI>loads the "wrapped" activatable object's class and
+ * calls its activation constructor with the context classloader
+ * set to the newly created <code>ExportClassLoader</code>.
+ * <LI> resets the context class loader to the original
+ * context classloader
+ * </UL>
+ * The first instance of this class will also replace the VM's
+ * existing <code>Policy</code> object, if any,
+ * with a <code>AggregatePolicyProvider</code>.
+ *
+ * @param id The <code>ActivationID</code> of this object
+ * @param data The activation data for this object
+ * @throws java.lang.Exception
+ *
+ * @see com.sun.jini.start.ActivateWrapper.ExportClassLoader
+ * @see com.sun.jini.start.ActivateWrapper.ActivateDesc
+ * @see com.sun.jini.start.AggregatePolicyProvider
+ * @see com.sun.jini.start.SharedActivationPolicyPermission
+ * @see java.security.Policy
+ *
+ */
+ public ActivateWrapper(ActivationID id, MarshalledObject data)
+ throws Exception
+ {
+ try {
+ logger.entering(ActivateWrapper.class.getName(),
+ "ActivateWrapper", new Object[] { id, data });
+
+ ActivateDesc desc = (ActivateDesc)
+ new MarshalledInstance(data).get(false);
+ logger.log(Level.FINEST, "ActivateDesc: {0}", desc);
+
+ Thread t = Thread.currentThread();
+ ClassLoader ccl = t.getContextClassLoader();
+ logger.log(Level.FINEST, "Saved current context class loader: {0}",
+ ccl);
+
+ ExportClassLoader cl = null;
+ try {
+ cl = new ExportClassLoader(desc.importLocation,
+ desc.exportLocation,
+ ccl);
+ logger.log(Level.FINEST, "Created ExportClassLoader: {0}", cl);
+ } catch (Exception e) {
+ logger.throwing(ActivateWrapper.class.getName(),
+ "ActivateWrapper", e);
+ throw e;
+ }
+
+ checkPolicyPermission(desc.policy, desc.importLocation);
+
+ synchronized (ActivateWrapper.class) {
+ // supplant global policy 1st time through
+ if (globalPolicy == null) {
+ initialGlobalPolicy = Policy.getPolicy();
+ if (!(initialGlobalPolicy instanceof DynamicPolicy)) {
+ initialGlobalPolicy =
+ new DynamicPolicyProvider(initialGlobalPolicy);
+ }
+ globalPolicy =
+ new AggregatePolicyProvider(initialGlobalPolicy);
+ Policy.setPolicy(globalPolicy);
+ logger.log(Level.FINEST,
+ "Global policy set: {0}", globalPolicy);
+ }
+ Policy service_policy =
+ getServicePolicyProvider(
+ new PolicyFileProvider(desc.policy));
+ Policy backstop_policy =
+ getServicePolicyProvider(initialGlobalPolicy);
+ LoaderSplitPolicyProvider split_service_policy =
+ new LoaderSplitPolicyProvider(
+ cl, service_policy, backstop_policy);
+ /* Grant "this" code enough permission to do its work
+ * under the service policy, which takes effect (below)
+ * after the context loader is (re)set.
+ * Note: Throws UnsupportedOperationException if dynamic grants
+ * aren't supported (because underlying policies don't support it).
+ */
+ split_service_policy.grant(
+ this.getClass(),
+ null, /* Principal[] */
+ new Permission[] { new AllPermission() } );
+ globalPolicy.setPolicy(cl, split_service_policy);
+ logger.log(Level.FINEST,
+ "Added policy to set: {0}", desc.policy);
+ }
+
+ boolean initialize = false;
+ Class ac = ClassLoading.forName(desc.className, initialize, cl);
+ logger.log(Level.FINEST, "Obtained implementation class: {0}", ac);
+
+ t.setContextClassLoader(cl);
+
+ try {
+ logger.log(Level.FINEST,
+ "Set new context class loader: {0}", cl);
+ Constructor constructor =
+ ac.getDeclaredConstructor(actTypes);
+ logger.log(Level.FINEST,
+ "Obtained implementation constructor: {0}",
+ constructor);
+ constructor.setAccessible(true);
+ impl =
+ constructor.newInstance(new Object[]{id, desc.data});
+ if (impl instanceof Startable) {
+ ((Startable) impl).start();
+ } else {
+ logger.log( Level.SEVERE,
+ "Service {0} doesn''t implement {1} {2} {3} {4} {5} {6}",
+ new Object []
+ {
+ impl.getClass().getCanonicalName(),
+ Startable.class.getCanonicalName(),
+ "this service is likely to suffer from race",
+ "conditions caused by export performed during",
+ "construction, or threads started while ''this''",
+ "has been allowed to escape during construction",
+ "https://www.securecoding.cert.org/confluence/display/java/TSM01-J.+Do+not+let+the+this+reference+escape+during+object+construction"
+ }
+ );
+ }
+ logger.log(Level.FINEST,
+ "Obtained implementation instance: {0}", impl);
+ } finally {
+ t.setContextClassLoader(ccl);
+ logger.log(Level.FINEST, "Context class loader reset to: {0}",
+ ccl);
+ }
+ } catch (Exception e) {
+ logger.throwing(ActivateWrapper.class.getName(),
+ "ActivateWrapper", e);
+ throw e;
+ }
+ logger.exiting(ActivateWrapper.class.getName(),
+ "ActivateWrapper");
+ }
+
+
+ /**
+ * Return a reference to service being wrapped in place
+ * of this object.
+ */
+ private Object writeReplace() throws ObjectStreamException {
+ Object impl_proxy = impl;
+ if (impl instanceof ProxyAccessor) {
+ impl_proxy = ((ProxyAccessor) impl).getProxy();
+ logger.log(Level.FINEST,
+ "Obtained implementation proxy: {0}", impl_proxy);
+ if (impl_proxy == null) {
+ throw new InvalidObjectException(
+ "Implementation's getProxy() returned null");
+ }
+ }
+ return impl_proxy;
+ }
+
+ /**
+ * Analog to
+ * {@link java.rmi.activation.Activatable#register(java.rmi.activation.ActivationDesc)
+ * Activatable.register()} for activatable objects that want
+ * to use this wrapper mechanism.
+ *
+ * @return activation ID of the registered service
+ *
+ * @throws ActivationException if there was a problem registering
+ * the activatable class with the activation system
+ * @throws RemoteException if there was a problem communicating
+ * with the activation system
+ */
+ public static ActivationID register(ActivationGroupID gid,
+ ActivateDesc desc,
+ boolean restart,
+ ActivationSystem sys)
+ throws ActivationException, RemoteException
+ {
+ logger.entering(ActivateWrapper.class.getName(),
+ "register", new Object[] { gid, desc, Boolean.valueOf(restart), sys });
+
+ MarshalledObject data;
+ try {
+ data = new MarshalledInstance(desc).convertToMarshalledObject();
+ } catch (Exception e) {
+ MarshalException me =
+ new MarshalException("marshalling ActivateDesc", e);
+ logger.throwing(ActivateWrapper.class.getName(),
+ "register", me);
+ throw me;
+ }
+
+ ActivationDesc adesc =
+ new ActivationDesc(gid,
+ ActivateWrapper.class.getName(),
+ null,
+ data,
+ restart
+ );
+ logger.log(Level.FINEST,
+ "Registering descriptor with activation: {0}", adesc);
+
+ ActivationID aid = sys.registerObject(adesc);
+
+ logger.exiting(ActivateWrapper.class.getName(),
+ "register", aid);
+ return aid;
+ }
+
+ /**
+ * Checks that all the provided <code>URL</code>s have permission to
+ * use the given policy.
+ */
+ private static void checkPolicyPermission(String policy, URL[] urls) {
+ logger.entering(ActivateWrapper.class.getName(),
+ "checkPolicyPermission", new Object[] { policy, urlsToPath(urls) });
+ // Create desired permission object
+ Permission perm = new SharedActivationPolicyPermission(policy);
+ Certificate[] certs = null;
+ CodeSource cs;
+ ProtectionDomain pd;
+ // Loop over all codebases
+ int l = urls.length;
+ for (int i=0; i < l; i++) {
+ // Create ProtectionDomain for given codesource
+ cs = new CodeSource(urls[i], certs);
+ pd = new ProtectionDomain(cs, null, null, null);
+ logger.log(Level.FINEST,
+ "Checking protection domain: {0}", pd);
+
+ // Check if current domain allows desired permission
+ if(!pd.implies(perm)) {
+ SecurityException se = new SecurityException(
+ "ProtectionDomain " + pd
+ + " does not have required permission: " + perm);
+ logger.throwing(ActivateWrapper.class.getName(),
+ "checkPolicyPermission", se);
+ throw se;
+ }
+ }
+ logger.exiting(ActivateWrapper.class.getName(),
+ "checkPolicyPermission");
+ }
+
+ /**
+ * Utility method that converts a <code>URL[]</code>
+ * into a corresponding, space-separated string with
+ * the same array elements.
+ *
+ * Note that if the array has zero elements, the return value is
+ * null, not the empty string.
+ */
+ private static String urlsToPath(URL[] urls) {
+//TODO - check if spaces in file paths are properly escaped (i.e.% chars)
+ if (urls == null) {
+ return null;
+ } else if (urls.length == 0) {
+ return "";
+ } else if (urls.length == 1) {
+ return urls[0].toExternalForm();
+ } else {
+ StringBuilder path = new StringBuilder(urls[0].toExternalForm());
+ for (int i = 1; i < urls.length; i++) {
+ path.append(' ');
+ path.append(urls[i].toExternalForm());
+ }
+ return path.toString();
+ }
+ }
+
+ static Policy getServicePolicyProvider(Policy service_policy) throws Exception {
+ Policy servicePolicyWrapper;
+ if (servicePolicyProvider != null) {
+ Class sp = Class.forName(servicePolicyProvider);
+ logger.log(Level.FINEST,
+ "Obtained custom service policy implementation class: {0}", sp);
+ Constructor constructor =
+ sp.getConstructor(policyTypes);
+ logger.log(Level.FINEST,
+ "Obtained custom service policy implementation constructor: {0}",
+ constructor);
+ servicePolicyWrapper = (Policy)
+ constructor.newInstance(new Object[]{service_policy});
+ logger.log(Level.FINEST,
+ "Obtained custom service policy implementation instance: {0}",
+ servicePolicyWrapper);
+ } else {
+ servicePolicyWrapper = new DynamicPolicyProvider(service_policy);
+ logger.log(Level.FINEST,
+ "Using default service policy implementation instance: {0}",
+ servicePolicyWrapper);
+ }
+ return servicePolicyWrapper;
+ }
+
+
+}