/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004, 2005, 2006], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program is distributed * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.hq.ui.action.resource.common.inventory; import javax.annotation.Resource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts2.interceptor.validation.SkipValidation; import org.hyperic.hq.appdef.ConfigResponseDB; import org.hyperic.hq.appdef.shared.AppdefEntityConstants; import org.hyperic.hq.appdef.shared.AppdefEntityID; import org.hyperic.hq.appdef.shared.AppdefResourceValue; import org.hyperic.hq.appdef.shared.ConfigFetchException; import org.hyperic.hq.appdef.shared.InvalidConfigException; import org.hyperic.hq.appdef.shared.ServerValue; import org.hyperic.hq.bizapp.shared.AllConfigResponses; import org.hyperic.hq.bizapp.shared.AppdefBoss; import org.hyperic.hq.bizapp.shared.ProductBoss; import org.hyperic.hq.product.PluginException; import org.hyperic.hq.product.PluginNotFoundException; import org.hyperic.hq.product.ProductPlugin; import org.hyperic.hq.ui.Constants; import org.hyperic.hq.ui.action.BaseActionNG; import org.hyperic.hq.ui.util.BizappUtilsNG; import org.hyperic.hq.ui.util.RequestUtils; import org.hyperic.util.config.ConfigResponse; import org.hyperic.util.config.ConfigSchema; import org.hyperic.util.config.EncodingException; import org.hyperic.util.config.InvalidOptionValueException; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import com.opensymphony.xwork2.ModelDriven; @Component("editConfigPropertiesActionNG") @Scope("prototype") public class EditConfigPropertiesActionNG extends BaseActionNG implements ModelDriven<ResourceConfigFormNG> { public static final String ERR_NOMSG = "resource.common.error.ConfigError.NoMessage"; public static final String ERR_CONFIG = "resource.common.error.ConfigError"; private final Log log = LogFactory.getLog(EditConfigPropertiesActionNG.class.getName()); @Resource private AppdefBoss appdefBoss; @Resource private ProductBoss productBoss; private ResourceConfigFormNG cfgForm = new ResourceConfigFormNG(); private String internalEid; private Integer internalRid; private Integer internalType; public String save() throws Exception { AppdefEntityID aeid = new AppdefEntityID(cfgForm.getType().intValue(), cfgForm.getRid()); request.setAttribute(Constants.ENTITY_ID_PARAM, aeid.getAppdefKey()); switch (aeid.getType()) { case AppdefEntityConstants.APPDEF_TYPE_PLATFORM: request.setAttribute(Constants.ACCORDION_PARAM, "5"); break; case AppdefEntityConstants.APPDEF_TYPE_SERVER: request.setAttribute(Constants.ACCORDION_PARAM, "3"); break; case AppdefEntityConstants.APPDEF_TYPE_SERVICE: request.setAttribute(Constants.ACCORDION_PARAM, "2"); break; } String forward = checkSubmit(cfgForm); AllConfigResponses allConfigs = new AllConfigResponses(); AllConfigResponses allConfigsRollback = new AllConfigResponses(); String prefix; if (forward != null) { if (request.getParameter("todash") != null) { // HACK to redirect back to Problem Resources portlet if the // request originated there. // return mapping.findForward("dash"); return "dashboard"; } return forward; } try { Integer sessionId = RequestUtils.getSessionId(request); int sessionInt = sessionId.intValue(); String[] cfgTypes = ProductPlugin.CONFIGURABLE_TYPES; int i, numConfigs = cfgTypes.length; ConfigSchema[] schemas = new ConfigSchema[cfgTypes.length]; ConfigResponse[] oldConfigs = new ConfigResponse[cfgTypes.length]; ConfigResponse[] newConfigs = new ConfigResponse[cfgTypes.length]; // Save the original resource allConfigs.setResource(aeid); allConfigsRollback.setResource(aeid); ConfigResponseDB oldConfig = productBoss.getConfigResponse(sessionInt, aeid); // get the configSchemas and existing configs for (i = 0; i < numConfigs; i++) { try { byte[] oldCfgBytes = null; if (cfgTypes[i].equals(ProductPlugin.TYPE_PRODUCT)) { oldCfgBytes = oldConfig.getProductResponse(); } else if (cfgTypes[i].equals(ProductPlugin.TYPE_MEASUREMENT)) { oldCfgBytes = oldConfig.getMeasurementResponse(); } else if (cfgTypes[i].equals(ProductPlugin.TYPE_CONTROL)) { oldCfgBytes = oldConfig.getControlResponse(); if ( (aeid.isPlatform()) && (oldCfgBytes == null)) { // in case of platform if there is no control config - control actions are not supported // notice - empty control response is created for server/service (not too good - we rely that in this case getPlugin of control // manager will return no supported - better not to create control plugin - in this case too.) -change for 6.0?? blankoutConfig(i, allConfigs, allConfigsRollback); if (log.isDebugEnabled()) { log.debug("Blanking for " + cfgTypes[i] + "->(not supported)") ; } continue; } } else if (cfgTypes[i].equals(ProductPlugin.TYPE_RESPONSE_TIME)) { oldCfgBytes = oldConfig.getResponseTimeResponse(); } if (oldCfgBytes == null) { oldConfigs[i] = new ConfigResponse(); } else { oldConfigs[i] = ConfigResponse.decode(oldCfgBytes); } schemas[i] = productBoss.getConfigSchema(sessionInt, aeid, cfgTypes[i], oldConfigs[i]); allConfigsRollback.setConfig(i, oldConfigs[i]); allConfigsRollback.setSupports(i, true); allConfigs.setSupports(i, true); } catch (PluginNotFoundException e) { // No plugin support for this config type - skip ahead to // the next type. log.debug(cfgTypes[i] + " PluginNotFound for " + aeid + " : " + e); log.debug("Blanking for " + cfgTypes[i] + "->(not supported)"); blankoutConfig(i, allConfigs, allConfigsRollback); } } AppdefResourceValue updatedResource = appdefBoss.findById(sessionInt, aeid); // get the new configs based on UI form fields for (i = 0; i < numConfigs; i++) { // Don't bother configuring things that aren't supported. if (!allConfigs.getSupports(i)) continue; // Load new configs from requestParams if (i == ProductPlugin.CFGTYPE_IDX_PRODUCT) { prefix = ProductPlugin.TYPE_PRODUCT + "."; newConfigs[i] = BizappUtilsNG.buildSaveConfigOptionsNG(prefix, request, new ConfigResponse(), schemas[i], null); // Make current form values appear in case of error. cfgForm.setResourceConfigOptions(BizappUtilsNG.buildLoadConfigOptions(prefix, schemas[i], newConfigs[i])); allConfigs.setShouldConfig(i, true); allConfigs.setConfig(i, newConfigs[i]); allConfigsRollback.setShouldConfig(i, true); } else { if (i == ProductPlugin.CFGTYPE_IDX_RESPONSE_TIME) { if (aeid.isServer() || aeid.isPlatform()) { // On servers, RT always gets an empty config allConfigs.setConfig(i, new ConfigResponse()); allConfigs.setShouldConfig(i, true); allConfigsRollback.setShouldConfig(i, true); } else if (!aeid.isService() || !allConfigs.supportsMetricConfig()) { // Otherwise, only configure response time for // services, and only if the plugin has metric // support. blankoutConfig(i, allConfigs, allConfigsRollback); continue; } } if (i == ProductPlugin.CFGTYPE_IDX_CONTROL && !aeid.isService() && !aeid.isServer() && !aeid.isPlatform() ) { // Control is only supported on platforms, servers and services blankoutConfig(i, allConfigs, allConfigsRollback); continue; } newConfigs[i] = new ConfigResponse(schemas[i]); allConfigs.setConfig(i, newConfigs[i]); prefix = cfgTypes[i] + "."; Boolean wereChanges = BizappUtilsNG.populateConfig(request, prefix, schemas[i], newConfigs[i], oldConfigs[i]); if (wereChanges == null) { // This means the schema had no options, so the concept // of changes doesn't make much sense. In these cases, // we'll just keep the empty ConfigResponse allConfigs.setShouldConfig(i, true); allConfigsRollback.setShouldConfig(i, true); } else if (wereChanges.equals(Boolean.TRUE)) { // There were changes. Cool, we'll keep them. allConfigs.setShouldConfig(i, true); allConfigsRollback.setShouldConfig(i, true); } else { // There were no changes. We discard the config // response. This way, when we do the server-side // bizapp stuff, we don't re-configure things that // have not changed. // But AHA! There is a perverted edge case here: if // this is a service and the config prop names for the // service // are the same as those for the server, and the service // has // never been configured, then we won't think anything // has // changed when in fact it has. See bug 8251. boolean reallyWereChanges = false; try { productBoss.getMergedConfigResponse(sessionInt, cfgTypes[i], aeid, true); } catch (ConfigFetchException cfe) { // OK, so there really were changes because the // config doesn't // exist! reallyWereChanges = true; allConfigs.setShouldConfig(i, true); allConfigsRollback.setShouldConfig(i, true); } if (!reallyWereChanges) { allConfigs.setShouldConfig(i, false); allConfigsRollback.setShouldConfig(i, false); } } switch (i) { case ProductPlugin.CFGTYPE_IDX_MEASUREMENT: // If metric config was setup, then setup runtime AI // flag. if ((aeid.isServer() || aeid.isService()) && (allConfigs.getMetricConfig() != null)) { boolean rollback, runtimeAI; if (aeid.isServer()) { rollback = ((ServerValue) updatedResource).getRuntimeAutodiscovery(); runtimeAI = cfgForm.getServerBasedAutoInventory(); } else { rollback = false; runtimeAI = true; // XXX // !((ServiceValue)updatedResource).getWasAutodiscovered(); } allConfigsRollback.setEnableRuntimeAIScan(rollback); allConfigs.setEnableRuntimeAIScan(runtimeAI); } cfgForm.setMonitorConfigOptions(BizappUtilsNG.buildLoadConfigOptions(prefix, schemas[i], newConfigs[i])); break; case ProductPlugin.CFGTYPE_IDX_CONTROL: cfgForm.setControlConfigOptions(BizappUtilsNG.buildLoadConfigOptions(prefix, schemas[i], newConfigs[i])); break; case ProductPlugin.CFGTYPE_IDX_RESPONSE_TIME: // enable the RT metrics based on input from ui allConfigs.setEnableServiceRT(cfgForm.getServiceRTEnabled()); allConfigs.setEnableEuRT(cfgForm.getEuRTEnabled()); cfgForm.setRtConfigOptions(BizappUtilsNG.buildLoadConfigOptions(prefix, schemas[i], newConfigs[i])); break; } } } // call the uber setter in the AppdefBoss appdefBoss.setAllConfigResponses(sessionInt, allConfigs, allConfigsRollback); addActionMessage(getText("resource." + aeid.getTypeName() + ".inventory.confirm.EditConfigProperties", new String[] {updatedResource.getName()}) ); // HACK to redirect back to Problem Resources portlet if the // request originated there. if (request.getParameter("todash") != null) { // HACK to redirect back to Problem Resources portlet if the // request originated there. // return mapping.findForward("dash"); return "dashboard"; } this.setEntityRequestParams(aeid); setTitleInfo(); return SUCCESS; } catch (InvalidConfigException e) { log.error("Invalid config " + e); setErrorWithNullCheck(e, ERR_NOMSG, ERR_CONFIG); // cfgForm.validationErrors = true; // return returnFailure(request, mapping); return INPUT; } catch (InvalidOptionValueException e) { log.error("Invalid config " + e); // RequestUtils.setErrorWithNullCheck(request, e, ERR_NOMSG, ERR_CONFIG); // return returnFailure(request, mapping); setErrorWithNullCheck(e, ERR_NOMSG, ERR_CONFIG); return INPUT; } catch (ConfigFetchException e) { log.error("General configuration set error " + e, e); // RequestUtils.setErrorWithNullCheck(request, e, ERR_NOMSG, ERR_CONFIG); // cfgForm.validationErrors = true; // return returnFailure(request, mapping); setErrorWithNullCheck(e, ERR_NOMSG, ERR_CONFIG); return INPUT; } catch (EncodingException e) { log.error("Encoding error " + e); // RequestUtils.setErrorWithNullCheck(request, e, ERR_NOMSG, ERR_CONFIG); // cfgForm.validationErrors = true; // return returnFailure(request, mapping); setErrorWithNullCheck(e, ERR_NOMSG, ERR_CONFIG); return INPUT; } catch (PluginNotFoundException e) { log.error("Plugin not found " + e); // RequestUtils.setErrorObject(request, "resource.common.inventory.error.PluginNotFound", e.getMessage()); // return returnFailure(request, mapping); setErrorObject("resource.common.inventory.error.PluginNotFound", e.getMessage()); return INPUT; } catch (PluginException e) { log.error("Exception occured in plugin " + e); // RequestUtils.setErrorObject(request, "resource.common.inventory.error.agentNotReachable", e.getMessage()); // cfgForm.validationErrors = true; // return returnFailure(request, mapping); setErrorObject( "resource.common.inventory.error.agentNotReachable", e.getMessage()); return INPUT; } } @SkipValidation public String cancel() throws Exception { setHeaderResources(); clearErrorsAndMessages(); AppdefEntityID aeid = RequestUtils.getEntityId(request); if (aeid!= null) { internalEid = aeid.toString(); } return "cancel"; } @SkipValidation public String reset() throws Exception { setHeaderResources(); cfgForm.reset(); clearErrorsAndMessages(); AppdefEntityID aeid = RequestUtils.getEntityId(request); if (aeid!= null) { setEntityRequestParams(aeid); } return "reset"; } public ResourceConfigFormNG getModel() { return cfgForm; } public ResourceConfigFormNG getCfgForm() { return cfgForm; } public void setCfgForm(ResourceConfigFormNG cfgForm) { this.cfgForm = cfgForm; } public String getInternalEid() { return internalEid; } public void setInternalEid(String internalEid) { this.internalEid = internalEid; } public Integer getInternalRid() { return internalRid; } public void setInternalRid(Integer internalRid) { this.internalRid = internalRid; } public Integer getInternalType() { return internalType; } public void setInternalType(Integer internalType) { this.internalType = internalType; } private void setEntityRequestParams (AppdefEntityID eid) { this.internalEid = eid.toString(); this.internalRid = eid.getId(); this.internalType = eid.getType(); } private void blankoutConfig(int i, AllConfigResponses c1, AllConfigResponses c2) { c1.setConfig(i, null); c2.setConfig(i, null); c1.setSupports(i, false); c2.setSupports(i, false); c1.setShouldConfig(i, false); c2.setShouldConfig(i, false); } private void setErrorWithNullCheck(Exception e, String nullMsg, String regularMsg) { try { if (e.getMessage().equals("null")) { addActionError(getText(nullMsg )); } else { addActionError(getText( regularMsg, new String[] {e.getMessage()}) ); } } catch (Exception npe) { addActionError(getText(nullMsg )); } } private void setErrorObject( String key, String regularMsg) { addActionError(getText( key, new String[] {regularMsg}) ); } private void setTitleInfo(){ request.setAttribute("titleKey",getText("resource.platform.inventory.ConfigurationPropertiesTitle")); request.setAttribute(Constants.TITLE_PARAM_ATTR, cfgForm.getName()); } }