/*
* 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.start.ServiceStarter;
import com.sun.jini.start.ServiceDescriptor;
import com.sun.jini.start.SharedActivatableServiceDescriptor;
import com.sun.jini.start.SharedActivatableServiceDescriptor.Created;
import net.jini.config.Configuration;
import net.jini.config.ConfigurationProvider;
import net.jini.config.ConfigurationException;
import java.io.File;
import java.rmi.activation.ActivationException;
import java.rmi.activation.ActivationSystem;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.MissingResourceException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.security.auth.Subject;
import com.sun.jini.system.FileSystem;
/**
* This class contains the command-line interface for
* destroying an instance of a shared activation group.
*
* The following items are discussed below:
* <ul>
* <li><a href="#configEntries">Configuring DestroySharedGroup</a>
* <li><a href="#logging">Logging</a>
* </ul>
*
* <a name="configEntries">
* <h3>Configuring DestroySharedGroup</h3>
* </a>
*
* This implementation of <code>DestroySharedGroup</code> supports the
* following configuration entries, with component
* <code>com.sun.jini.start</code>:
*
* <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. This entry is only
* used by the activatable implementation. <p>
*
* The service calls the {@link
* java.rmi.activation.ActivationSystem#unregisterObject
* unregisterObject} method on the {@link
* java.rmi.activation.ActivationSystem} when there is a problem
* creating a service.
* </table>
*
* <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
* services starter. If <code>null</code>, no JAAS login is performed.
* </table>
*
* <table summary="Describes the serviceDestructors 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>
* serviceDestructors</code></font>
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> {@link ServiceDescriptor}[]
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: no default
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description: <td> Array of service descriptors to start.
* </table>
*
*
*<a name="logging">
*<h3>Loggers and Logging Levels</h3>
*</a>
*
*The DestroySharedGroup service implementation uses the {@link
*java.util.logging.Logger}, named
* <code>com.sun.jini.start.service.starter</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 service.starter at different
* logging levels">
*
* <caption halign="center" valign="top"><b><code>
* com.sun.jini.start.service.starter</code></b></caption>
*
* <tr> <th scope="col"> Level <th scope="col"> Description
*
* <tr> <td> {@link java.util.logging.Level#SEVERE SEVERE} <td>
* for problems that prevent service destruction from proceeding
* <tr> <td> {@link java.util.logging.Level#WARNING WARNING} <td>
* for problems with service destruction that don't prevent further
* processing
* <tr> <td> {@link java.util.logging.Level#FINER FINER} <td>
* for high level
* service destruction operation tracing
* <tr> <td> {@link java.util.logging.Level#FINEST FINEST} <td>
* for low level
* service destruction operation tracing
*
* </table> <p>
*
* @author Sun Microsystems, Inc.
*
* @see com.sun.jini.start.ServiceStarter
*
* @since 1.2
*/
public class DestroySharedGroup {
/** Configure logger. */
private static final Logger logger = ServiceStarter.logger;
// Private constructor to prevent instantiation
private DestroySharedGroup() { }
/**
* The main method for the <code>DestroySharedGroup</code> application.
* The <code>args</code> parameter is passed directly to
* <code>ConfigurationProvider.getInstance()</code> in order to
* obtain a <code>Configuration</code> object. This configuration
* object is then queried for a
* <code>com.sun.jini.start.serviceDestructors</code> entry, which
* is assumed to be a <code>SharedActivatableServiceDescriptor[]</code>
* configured to run {@link com.sun.jini.start.SharedGroup} implementations.
* The {@link com.sun.jini.start.SharedGroup#destroyVM() destroyVM()}
* method is then called on each of the array elements. An attempt is
* made to also delete shared group <code>log</code> directory associated
* with each array element.
* @param args <code>String[]</code> passed to
* <code>ConfigurationProvider.getInstance()</code> in order
* to obtain a <code>Configuration</code> object.
*
* @see ServiceDescriptor
* @see SharedActivatableServiceDescriptor
* @see net.jini.config.Configuration
* @see net.jini.config.ConfigurationProvider
*
*/
public static void main(String[] args) {
ServiceStarter.ensureSecurityManager();
logger.entering(DestroySharedGroup.class.getName(), "main",
((Object[])args));
try {
Configuration config = ConfigurationProvider.getInstance(args);
ServiceDescriptor[] srvArray =
(ServiceDescriptor[])config.getEntry(
ServiceStarter.START_PACKAGE, "serviceDestructors",
ServiceDescriptor[].class, null);
if (srvArray == null || srvArray.length == 0) {
logger.log(Level.WARNING, "service.config.empty");
return;
}
LoginContext loginContext = (LoginContext)
config.getEntry(ServiceStarter.START_PACKAGE, "loginContext",
LoginContext.class, null);
if (loginContext != null)
destroyWithLogin(srvArray, config, loginContext);
else
destroy(srvArray, config);
} catch (ConfigurationException ce) {
logger.log(Level.SEVERE, "destroy.config.exception", ce);
} catch (Exception e) {
logger.log(Level.SEVERE, "destroy.unexpected.exception", e);
}
logger.exiting(DestroySharedGroup.class.getName(), "main");
}
/**
* Method that attempts to destroy any available <code>SharedGroup</code>
* objects in the provided <code>ServiceDescriptor[]</code>.
*/
private static void destroy(ServiceDescriptor[] srvArray,
Configuration config) throws Exception
{
logger.entering(DestroySharedGroup.class.getName(), "destroy",
new Object[] {Arrays.asList(srvArray), config} );
Created created = null;
SharedActivatableServiceDescriptor desc = null;
ActivationSystem activationSystem = null;
for (int i=0; i < srvArray.length; i++) {
if (srvArray[i] instanceof SharedActivatableServiceDescriptor) {
desc = (SharedActivatableServiceDescriptor)srvArray[i];
activationSystem =
ServiceStarter.getActivationSystem(
desc.getActivationSystemHost(),
desc.getActivationSystemPort(),
config);
try {
created = (Created)desc.create(config);
if (created != null &&
created.proxy instanceof SharedGroup) {
// service proxy from create() is already prepared
SharedGroup sg = (SharedGroup)created.proxy;
try {
sg.destroyVM();
try {
File log = new File(desc.getSharedGroupLog());
FileSystem.destroy(log, true);
} catch (Exception e) {
logger.log(Level.WARNING,
"destroy.group.deletion", e);
}
} catch (Exception e ) {
logger.log(Level.SEVERE,
"destroy.group.exception", e);
//TODO - Add configurable retry logic or just unregister
}
} else {
logger.log(Level.WARNING, "destroy.unexpected.proxy",
(created==null)?null:created.proxy);
if (created != null && created.aid != null) {
try {
activationSystem.unregisterObject(created.aid);
} catch (Exception e) {
logger.log(Level.WARNING,
"destroy.unregister.exception", e) ;
}
}
}
} catch (Exception ee) {
logger.log(Level.SEVERE, "destroy.creation.exception", ee);
if (created != null && created.aid != null) {
try {
activationSystem.unregisterObject(created.aid);
} catch (Exception e) {
logger.log(Level.WARNING,
"destroy.unregister.exception", ee) ;
}
}
}
} else {
logger.log(Level.WARNING, "destroy.unexpected.type", srvArray[i]);
}
}
logger.exiting(DestroySharedGroup.class.getName(), "destroy");
}
/**
* Method that attempts to login via the provided
* <code>LoginContext</code> and then calls <code>destroy</code>.
*/
private static void destroyWithLogin(
final ServiceDescriptor[] descs, final Configuration config,
final LoginContext loginContext) throws Exception
{
logger.entering(DestroySharedGroup.class.getName(),
"destroyWithLogin", new Object[] {descs, config, loginContext});
loginContext.login();
try {
Subject.doAsPrivileged(
loginContext.getSubject(),
new PrivilegedExceptionAction() {
public Object run() throws Exception
{
destroy(descs, config);
return null;
}
},
null);
} catch (PrivilegedActionException e) {
throw e.getException();
} finally {
try {
loginContext.logout();
} catch (LoginException le) {
logger.log(Level.FINE, "service.logout.exception", le);
}
}
logger.exiting(DestroySharedGroup.class.getName(),
"destroyWithLogin");
return;
}
}