/* * 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 com.sun.jini.constants.TimeConstants; import com.sun.jini.system.FileSystem; import net.jini.activation.ActivationExporter; import net.jini.config.Configuration; import net.jini.config.ConfigurationProvider; import net.jini.config.ConfigurationException; import net.jini.core.constraint.RemoteMethodControl; import net.jini.export.Exporter; import net.jini.export.ProxyAccessor; import net.jini.jeri.BasicILFactory; import net.jini.jeri.BasicJeriExporter; import net.jini.jeri.tcp.TcpServerEndpoint; import net.jini.security.BasicProxyPreparer; import net.jini.security.ProxyPreparer; import net.jini.security.TrustVerifier; import net.jini.security.proxytrust.ServerProxyTrust; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.rmi.MarshalledObject; import java.rmi.NoSuchObjectException; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.activation.Activatable; import java.rmi.activation.ActivationGroup; import java.rmi.activation.ActivationGroupID; import java.rmi.activation.ActivationID; import java.rmi.activation.ActivationSystem; import java.rmi.activation.ActivationException; import java.rmi.server.UnicastRemoteObject; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.logging.Level; import java.util.logging.Logger; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; /** * The provided implementation * of the {@link SharedGroup} service. * * The following items are discussed below: * <ul> * <li><a href="#configEntries">Configuring SharedGroupImpl</a> * <li><a href="#logging">Logging</a> * </ul> * * <a name="configEntries"> * <h3>Configuring SharedGroupImpl</h3> * </a> * * This implementation of <code>SharedGroupImpl</code> supports the * following configuration entries, with component * <code>com.sun.jini.start</code>: * * <table summary="Describes the activationIdPreparer configuration entry" * 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> * activationIdPreparer</code></font> * <tr valign="top"> <td>   <th scope="row" align="right"> * Type: <td> {@link net.jini.security.ProxyPreparer} * <tr valign="top"> <td>   <th scope="row" align="right"> * Default: <td> <code> * new {@link net.jini.security.BasicProxyPreparer}() * </code> * <tr valign="top"> <td>   <th scope="row" align="right"> * Description: <td> The proxy preparer for the service's activation * ID. The value should not be <code>null</code>. * * The service starter calls the * {@link java.rmi.activation.ActivationID#activate * activate} method to activate the service. * </table> * * <table summary="Describes the activationSystemPreparer configuration * entry" * 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> * activationSystemPreparer</code></font> * <tr valign="top"> <td>   <th scope="row" align="right"> * Type: <td> {@link net.jini.security.ProxyPreparer} * <tr valign="top"> <td>   <th scope="row" align="right"> * Default: <td> <code> * new {@link net.jini.security.BasicProxyPreparer}()</code> * <tr valign="top"> <td>   <th scope="row" align="right"> * Description: <td> The proxy preparer for the proxy for the * activation system. The value should not be <code>null</code>. This * entry is obtained at service start and restart. * * The service calls the {@link * java.rmi.activation.ActivationSystem#unregisterGroup * unregisterGroup} method on the {@link * java.rmi.activation.ActivationSystem} upon service destruction. * </table> * * <table summary="Describes the exporter configuration entry" * 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> * exporter</code></font> * <tr valign="top"> <td>   <th scope="row" align="right"> * Type: <td> {@link net.jini.export.Exporter} * <tr valign="top"> <td>   <th scope="row" align="right"> * Default: <td> * <pre> * new {@link net.jini.activation.ActivationExporter}( * <i>activationID</i>, * new {@link net.jini.jeri.BasicJeriExporter}( * {@link net.jini.jeri.tcp.TcpServerEndpoint#getInstance TcpServerEndpoint.getInstance}(0), * new {@link net.jini.jeri.BasicILFactory}(), false, true)) * </pre> * <tr valign="top"> <td>   <th scope="row" align="right"> * Description: <td> The object to use for exporting the service. The * value should not be <code>null</code>. The call to * <code>getEntry</code> will supply the activation ID in * the <code>data</code> argument. This entry is obtained at service * start and restart. * </table> <p> * * <table summary="Describes the loginContext configuration entry" * 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> * loginContext</code></font> * <tr valign="top"> <td>   <th scope="row" align="right"> * Type: <td> {@link javax.security.auth.login.LoginContext} * <tr valign="top"> <td>   <th scope="row" align="right"> * Default: <td> <code>null</code> * <tr valign="top"> <td>   <th scope="row" align="right"> * Description: <td> If not <code>null</code>, specifies the JAAS * login context to use for performing a JAAS login and supplying the * {@link javax.security.auth.Subject} to use when running the * service. If <code>null</code>, no JAAS login is performed. This * entry is obtained at service start and restart. * </table> * * *<a name="logging"> *<h3>Loggers and Logging Levels</h3> *</a> * *The SharedGroupImpl service implementation uses the {@link *java.util.logging.Logger}, named <code>com.sun.jini.sharedGroup</code>. *The following table describes the *type of information logged as well as the levels of information logged. *<p> * * <table border="1" cellpadding="5" * summary="Describes logging performed by sharedGroup at different * logging levels"> * * <caption halign="center" valign="top"><b><code> * com.sun.jini.start.sharedGroup</code></b></caption> * * <tr> <th scope="col"> Level <th scope="col"> Description * * <tr> <td> {@link java.util.logging.Level#FINE FINE} <td> * for low level * service operation tracing * <tr> <td> {@link java.util.logging.Level#FINER FINER} <td> * for lower level * service operation tracing * <tr> <td> {@link java.util.logging.Level#FINEST FINEST} <td> * for lowest level * service operation tracing * * </table> <p> * * @author Sun Microsystems, Inc. * */ public class SharedGroupImpl implements SharedGroupBackEnd, ServerProxyTrust, ProxyAccessor { /** Component name for configuration entries */ private static final String START_PACKAGE = "com.sun.jini.start"; /** Configure logger */ private static final Logger logger = Logger.getLogger(START_PACKAGE + ".sharedGroup"); /** Our prepared activation ID reference */ private ActivationID activationID = null; /** The prepared activation system reference */ private ActivationSystem activationSystem; /** The inner proxy of this server */ private Remote ourStub; /** <code>LoginContext</code> for this service. */ private final LoginContext loginContext; /** The exporter for exporting and unexporting */ protected Exporter exporter; /** * Activation constructor. */ private SharedGroupImpl(ActivationID activationID, MarshalledObject data) throws Exception { logger.entering(SharedGroupImpl.class.getName(), "SharedGroupImpl", new Object[] { activationID, data}); this.activationID = activationID; try { String[] configArgs = (String[])data.get(); final Configuration config = ConfigurationProvider.getInstance(configArgs); loginContext = (LoginContext) config.getEntry( START_PACKAGE, "loginContext", LoginContext.class, null); if (loginContext != null) { doInitWithLogin(config, loginContext); } else { doInit(config); } } catch (Exception e) { cleanup(); throw e; } logger.exiting(SharedGroupImpl.class.getName(), "SharedGroupImpl"); } private void doInitWithLogin(final Configuration config, LoginContext loginContext) throws Exception { loginContext.login(); try { Subject.doAsPrivileged( loginContext.getSubject(), new PrivilegedExceptionAction() { public Object run() throws Exception { doInit(config); return null; } }, null); } catch (PrivilegedActionException e) { throw e.getException(); } } private void doInit(Configuration config) throws Exception { ProxyPreparer activationSystemPreparer = (ProxyPreparer) config.getEntry( START_PACKAGE, "activationSystemPreparer", ProxyPreparer.class, new BasicProxyPreparer()); if (activationSystemPreparer == null) { throw new ConfigurationException(START_PACKAGE + ".activationSystemPreparer entry should not be null"); } logger.log(Level.FINE, START_PACKAGE + ".activationSystemPreparer: {0}", activationSystemPreparer); ProxyPreparer activationIdPreparer = (ProxyPreparer) config.getEntry(START_PACKAGE, "activationIdPreparer", ProxyPreparer.class, new BasicProxyPreparer()); if (activationIdPreparer == null) { throw new ConfigurationException(START_PACKAGE + ".activationIdPreparer entry should not be null"); } logger.log(Level.FINE, START_PACKAGE + ".activationIdPreparer: {0}", activationIdPreparer); // Prepare activation subsystem /* * ActivationGroup is trusted and returned ActivationSystem * might already have been prepared by the group itself. */ activationSystem = (ActivationSystem) activationSystemPreparer.prepareProxy( ActivationGroup.getSystem()); logger.log(Level.FINE, "Prepared ActivationSystem: {0}", activationSystem); activationID = (ActivationID) activationIdPreparer.prepareProxy(activationID); logger.log(Level.FINEST, "Prepared ActivationID: {0}", activationID); /** * Would like to get this entry sooner, but need to use * the prepared activationID. */ exporter = (Exporter) config.getEntry( START_PACKAGE, "exporter", Exporter.class, new ActivationExporter( activationID, new BasicJeriExporter( TcpServerEndpoint.getInstance(0), new BasicILFactory(), false, true)), activationID); if (exporter == null) { throw new ConfigurationException(START_PACKAGE + ".exporter entry should not be null"); } logger.log(Level.FINE, START_PACKAGE + ".exporter: {0}", exporter); // Export service ourStub = exporter.export(this); logger.log(Level.FINEST, "Exported service proxy: {0}", ourStub); } // javadoc inherited from supertype public void destroyVM() throws RemoteException, ActivationException { logger.entering(SharedGroupImpl.class.getName(), "destroyVM"); /* * Would like to synch access to activationSystem, but need * to avoid holding locks across remote invocations. */ if (activationSystem != null) { activationSystem.unregisterGroup( ActivationGroup.currentGroupID()); logger.finest("ActivationGroup unregistered."); /* Unregistering the group implicitly unregisters * all the objects associated with that group as well. */ activationSystem = null; } (new DestroyThread()).start(); logger.exiting(SharedGroupImpl.class.getName(), "destroyVM"); } /** * Private utility method which attempts to roll back from * from a failed initialization attempt. */ private void cleanup() { logger.entering(SharedGroupImpl.class.getName(), "cleanup"); /* * Caller decides whether or not to unregister this object. */ if (exporter != null) { try { // Unexport object so that no new calls come in exporter.unexport(true); logger.finest("SharedGroupImpl unexported."); } catch (Exception e) { logger.log(Level.FINEST, "Problem unexporting SharedGroupImpl.", e); } } if (loginContext != null) { try { loginContext.logout(); logger.finest("SharedGroupImpl logged-out."); } catch (Exception e) { logger.log(Level.FINEST, "Problem logging out for SharedGroupImpl.", e); } } logger.exiting(SharedGroupImpl.class.getName(), "cleanup"); } /** * Termination thread code. We do this in a separate thread to * allow the calling thread to return normally. This is not guaranteed * since it's still possible for the VM to exit before the calling * thread returns. */ private class DestroyThread extends Thread { /** Create a non-daemon thread */ public DestroyThread() { super("DestroyThread"); /* override inheritance from RMI daemon thread */ setDaemon(false); } public void run() { logger.entering(DestroyThread.class.getName(), "run"); logger.finest("Calling System.exit() ..."); /* * Forcefully destroy the VM, in case there are any lingering * threads. */ System.exit(0); } } // inherit javadoc public Object getProxy() { logger.entering(SharedGroupImpl.class.getName(), "getProxy"); logger.exiting(SharedGroupImpl.class.getName(), "getProxy", ourStub); return ourStub; } ////////////////////////////////////////// // ProxyTrust Method ////////////////////////////////////////// //inherit javadoc public TrustVerifier getProxyVerifier( ) { /* No verifier if the server isn't secure */ if (!(ourStub instanceof RemoteMethodControl)) { throw new UnsupportedOperationException(); } else { return new ProxyVerifier((SharedGroupBackEnd)ourStub); } } }