| /* |
| * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0, which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| package com.sun.enterprise.config.modularity.command; |
| |
| import static com.sun.enterprise.config.util.ConfigApiLoggerInfo.*; |
| |
| import com.sun.enterprise.config.modularity.ConfigModularityUtils; |
| import com.sun.enterprise.config.modularity.customization.ConfigBeanDefaultValue; |
| import com.sun.enterprise.config.modularity.customization.ConfigCustomizationToken; |
| import com.sun.enterprise.config.serverbeans.Config; |
| import com.sun.enterprise.config.serverbeans.Domain; |
| import com.sun.enterprise.config.serverbeans.DomainExtension; |
| import com.sun.enterprise.config.serverbeans.SystemPropertyBag; |
| import com.sun.enterprise.util.LocalStringManagerImpl; |
| import com.sun.enterprise.util.SystemPropertyConstants; |
| |
| import org.glassfish.api.ActionReport; |
| import org.glassfish.api.I18n; |
| import org.glassfish.api.Param; |
| import org.glassfish.api.admin.AccessRequired; |
| import org.glassfish.api.admin.AdminCommand; |
| import org.glassfish.api.admin.AdminCommandContext; |
| import org.glassfish.api.admin.AdminCommandSecurity; |
| import org.glassfish.api.admin.ExecuteOn; |
| import org.glassfish.api.admin.RuntimeType; |
| import org.glassfish.api.admin.ServerEnvironment; |
| import org.glassfish.api.admin.config.ConfigExtension; |
| import org.glassfish.config.support.CommandTarget; |
| import org.glassfish.config.support.GlassFishConfigBean; |
| import org.glassfish.config.support.TargetType; |
| import org.glassfish.hk2.api.PerLookup; |
| import org.glassfish.hk2.api.ServiceLocator; |
| import org.jvnet.hk2.annotations.Service; |
| import org.jvnet.hk2.config.ConfigBeanProxy; |
| import org.jvnet.hk2.config.ConfigSupport; |
| import org.jvnet.hk2.config.SingleConfigCode; |
| import org.jvnet.hk2.config.TransactionFailure; |
| |
| import jakarta.inject.Inject; |
| import jakarta.inject.Named; |
| |
| import java.beans.PropertyVetoException; |
| import java.lang.reflect.Method; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| /** |
| * A remote command to delete a module's configuration. |
| * |
| * @author Masoud Kalali |
| */ |
| @TargetType(value = { CommandTarget.DAS, CommandTarget.CLUSTER, CommandTarget.CONFIG, CommandTarget.STANDALONE_INSTANCE, |
| CommandTarget.DOMAIN }) |
| @ExecuteOn(RuntimeType.ALL) |
| @Service(name = "delete-module-config") |
| @PerLookup |
| @I18n("delete.module.config") |
| public final class DeleteModuleConfigCommand extends AbstractConfigModularityCommand |
| implements AdminCommand, AdminCommandSecurity.Preauthorization, AdminCommandSecurity.AccessCheckProvider { |
| private final Logger LOG = getLogger(); |
| final private static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(DeleteModuleConfigCommand.class); |
| private static final String DEFAULT_FORMAT = ""; |
| private ActionReport report; |
| |
| @Inject |
| private Domain domain; |
| |
| @Inject |
| private ServiceLocator serviceLocator; |
| |
| @Inject |
| private ConfigModularityUtils configModularityUtils; |
| |
| @Param(name = "target", optional = true, defaultValue = SystemPropertyConstants.DAS_SERVER_NAME) |
| String target; |
| |
| @Inject |
| @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME) |
| Config config; |
| |
| @Param(name = "serviceName", primary = true) |
| private String serviceName; |
| |
| @Inject |
| ServerEnvironment serverenv; |
| |
| @Override |
| public void execute(AdminCommandContext context) { |
| report = context.getActionReport(); |
| if (target != null) { |
| Config newConfig = getConfigForName(target, serviceLocator, domain); |
| if (newConfig != null) { |
| config = newConfig; |
| } |
| if (config == null) { |
| report.setMessage(localStrings.getLocalString("delete.module.config.target.name.invalid", |
| "The target name specified is invalid. Please double check the target name and try again.")); |
| report.setActionExitCode(ActionReport.ExitCode.FAILURE); |
| return; |
| } |
| } |
| |
| if (serviceName == null) { |
| report.setMessage(localStrings.getLocalString("delete.module.config.service.name.is.required", |
| "The service name is required, please specify which service you want to delete its default configuration.")); |
| report.setActionExitCode(ActionReport.ExitCode.FAILURE); |
| return; |
| } |
| |
| final String className = configModularityUtils.convertConfigElementNameToClassName(serviceName); |
| Class configBeanType = configModularityUtils.getClassFor(serviceName); |
| if (configBeanType == null) { |
| String msg = localStrings.getLocalString("delete.module.config.not.such.a.service.found", |
| "Your service name does not match any service installed on this domain."); |
| report.setActionExitCode(ActionReport.ExitCode.FAILURE); |
| report.setMessage(msg); |
| return; |
| } |
| |
| if (configModularityUtils.hasCustomConfig(configBeanType)) { |
| List<ConfigBeanDefaultValue> defaults = configModularityUtils.getDefaultConfigurations(configBeanType, |
| configModularityUtils.getRuntimeTypePrefix(serverenv.getStartupContext())); |
| deleteDependentConfigElements(defaults); |
| } else { |
| deleteTopLevelExtensionByType(config, className, configBeanType); |
| } |
| } |
| |
| private void deleteDependentConfigElements(final List<ConfigBeanDefaultValue> defaults) { |
| for (ConfigBeanDefaultValue configBeanDefaultValue : defaults) { |
| deleteDependentConfigElement(configBeanDefaultValue); |
| } |
| } |
| |
| private void deleteDependentConfigElement(final ConfigBeanDefaultValue defaultValue) { |
| Class parentClass = configModularityUtils.getOwningClassForLocation(defaultValue.getLocation()); |
| final Class configBeanClass = configModularityUtils.getClassForFullName(defaultValue.getConfigBeanClassName()); |
| final Method m = configModularityUtils.findSuitableCollectionGetter(parentClass, configBeanClass); |
| if (m != null) { |
| try { |
| final ConfigBeanProxy parent = configModularityUtils.getOwningObject(defaultValue.getLocation()); |
| ConfigSupport.apply(new SingleConfigCode<ConfigBeanProxy>() { |
| @Override |
| public Object run(ConfigBeanProxy param) throws PropertyVetoException, TransactionFailure { |
| List col = null; |
| ConfigBeanProxy configBean = null; |
| try { |
| col = (List) m.invoke(param); |
| if (col != null) { |
| configBean = configModularityUtils.getCurrentConfigBeanForDefaultValue(defaultValue); |
| } |
| } catch (Exception e) { |
| String message = localStrings.getLocalString("delete.module.config.failed.deleting.dependant", |
| "Failed to remove all configuration elements related to your service form domain.xml. You can use create-module-config --dryRun with your module name to see all relevant configurations and try removing the config elements "); |
| report.setMessage(message); |
| report.setActionExitCode(ActionReport.ExitCode.FAILURE); |
| LOG.log(Level.INFO, DELETE_MODULE_CONFIG_FAILED_DELETING_DEPENDENT, e); |
| } |
| |
| if (configBean != null) { |
| boolean deleted = configModularityUtils.deleteConfigurationForConfigBean(configBean, col, defaultValue); |
| if (!deleted) { |
| for (int i = 0; i < col.size(); i++) { |
| if (configBeanClass.isAssignableFrom(col.get(i).getClass())) { |
| col.remove(col.get(i)); |
| removeCustomTokens(defaultValue, configBean, parent); |
| return param; |
| } |
| } |
| } |
| |
| } |
| return param; |
| } |
| }, parent); |
| } catch (Exception e) { |
| String message = localStrings.getLocalString("delete.module.config.failed.deleting.dependant", |
| "Failed to remove all configuration elements related to your service form domain.xml. You can use create-module-config --dryRun with your module name to see all relevant configurations and try removing the config elements "); |
| report.setMessage(message); |
| report.setActionExitCode(ActionReport.ExitCode.FAILURE); |
| LOG.log(Level.INFO, DELETE_MODULE_CONFIG_FAILED_DELETING_DEPENDENT, e); |
| |
| } |
| } else { |
| report.setMessage(localStrings.getLocalString("delete.module.config.failed.deleting.dependant", |
| "Failed to remove all configuration elements related to your service form domain.xml. You can use create-module-config --dryRun with your module name to see all relevant configurations and try removing the config elements ")); |
| report.setActionExitCode(ActionReport.ExitCode.FAILURE); |
| } |
| } |
| |
| private void deleteTopLevelExtensionByType(Config config, final String className, Class configBeanType) { |
| if (ConfigExtension.class.isAssignableFrom(configBeanType)) { |
| if (config.checkIfExtensionExists(configBeanType)) { |
| try { |
| ConfigSupport.apply(new SingleConfigCode<Config>() { |
| @Override |
| public Object run(Config param) throws PropertyVetoException, TransactionFailure { |
| List<ConfigExtension> configExtensions; |
| configExtensions = param.getExtensions(); |
| for (ConfigExtension ext : configExtensions) { |
| String configExtensionClass = GlassFishConfigBean.unwrap(ext).getProxyType().getSimpleName(); |
| if (configExtensionClass.equals(className)) { |
| configExtensions.remove(ext); |
| break; |
| } |
| } |
| return configExtensions; |
| } |
| }, config); |
| report.setActionExitCode(ActionReport.ExitCode.SUCCESS); |
| } catch (TransactionFailure e) { |
| String actual = e.getMessage(); |
| String msg = localStrings.getLocalString("delete.module.config.failed.to.delete.config", DEFAULT_FORMAT, serviceName, |
| actual); |
| report.setMessage(msg); |
| report.setActionExitCode(ActionReport.ExitCode.FAILURE); |
| report.setFailureCause(e); |
| } |
| } else { |
| report.setMessage(localStrings.getLocalString("delete.module.config.no.configuration", |
| "No customized configuration exist for this service nor the default configuration has been added to the domain.xml.")); |
| report.setActionExitCode(ActionReport.ExitCode.FAILURE); |
| } |
| |
| } else if (DomainExtension.class.isAssignableFrom(configBeanType)) { |
| if (domain.checkIfExtensionExists(configBeanType)) { |
| try { |
| ConfigSupport.apply(new SingleConfigCode<Domain>() { |
| @Override |
| public Object run(Domain param) throws PropertyVetoException, TransactionFailure { |
| List<DomainExtension> domainExtensions; |
| domainExtensions = param.getExtensions(); |
| for (DomainExtension ext : domainExtensions) { |
| String configExtensionClass = GlassFishConfigBean.unwrap(ext).getProxyType().getSimpleName(); |
| if (configExtensionClass.equals(className)) { |
| domainExtensions.remove(ext); |
| break; |
| } |
| } |
| return domainExtensions; |
| } |
| }, domain); |
| report.setActionExitCode(ActionReport.ExitCode.SUCCESS); |
| } catch (TransactionFailure e) { |
| String actual = e.getMessage(); |
| String msg = localStrings.getLocalString("delete.module.config.failed.to.delete.config", DEFAULT_FORMAT, serviceName, |
| actual); |
| report.setMessage(msg); |
| report.setActionExitCode(ActionReport.ExitCode.FAILURE); |
| report.setFailureCause(e); |
| } |
| } else { |
| report.setMessage(localStrings.getLocalString("delete.module.config.no.configuration", |
| "No customized configuration exist for this service nor the default configuration has been added to the domain.xml.")); |
| report.setActionExitCode(ActionReport.ExitCode.FAILURE); |
| } |
| |
| } |
| } |
| |
| private static <T extends ConfigBeanProxy> boolean removeCustomTokens(final ConfigBeanDefaultValue configBeanDefaultValue, |
| T finalConfigBean, ConfigBeanProxy parent) throws TransactionFailure, PropertyVetoException { |
| if (parent instanceof SystemPropertyBag) { |
| removeSystemPropertyForTokens(configBeanDefaultValue.getCustomizationTokens(), (SystemPropertyBag) parent); |
| return true; |
| } else { |
| ConfigBeanProxy curParent = finalConfigBean; |
| while (!(curParent instanceof SystemPropertyBag)) { |
| curParent = curParent.getParent(); |
| } |
| if (configBeanDefaultValue.getCustomizationTokens().size() != 0) { |
| final SystemPropertyBag bag = (SystemPropertyBag) curParent; |
| final List<ConfigCustomizationToken> tokens = configBeanDefaultValue.getCustomizationTokens(); |
| removeSystemPropertyForTokens(tokens, bag); |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| private static void removeSystemPropertyForTokens(List<ConfigCustomizationToken> tokens, SystemPropertyBag bag) |
| throws TransactionFailure { |
| for (ConfigCustomizationToken token : tokens) { |
| if (bag.containsProperty(token.getName())) { |
| bag.getSystemProperty().remove(bag.getSystemProperty(token.getName())); |
| } |
| } |
| } |
| |
| @Override |
| public Collection<? extends AccessRequired.AccessCheck> getAccessChecks() { |
| Class configBeanType = configModularityUtils.getClassFor(serviceName); |
| if (configBeanType == null) { |
| //TODO check if this is the correct course of action. |
| return Collections.emptyList(); |
| } |
| |
| if (configModularityUtils.hasCustomConfig(configBeanType)) { |
| List<ConfigBeanDefaultValue> defaults = configModularityUtils.getDefaultConfigurations(configBeanType, |
| configModularityUtils.getRuntimeTypePrefix(serverenv.getStartupContext())); |
| return getAccessChecksForDefaultValue(defaults, target, Arrays.asList("read", "delete")); |
| } |
| |
| if (ConfigExtension.class.isAssignableFrom(configBeanType)) { |
| return getAccessChecksForConfigBean(config.getExtensionByType(configBeanType), target, Arrays.asList("read", "delete")); |
| } |
| if (configBeanType.isAssignableFrom(DomainExtension.class)) { |
| return getAccessChecksForConfigBean(config.getExtensionByType(configBeanType), target, Arrays.asList("read", "delete")); |
| } |
| //TODO check if this is right course of action |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| public boolean preAuthorization(AdminCommandContext context) { |
| //TODO check if it is actually required to use this method to infer the resources etc or only using the |
| return true; |
| } |
| } |