/*
* Jopr Management Platform
* Copyright (C) 2005-2011 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also 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 and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.plugins.jbosscache;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.pluginapi.util.SelectiveSkippingEntityResolver;
/**
* A utility for creating or updating JBossCache 2.x XML configuration files.
*
* @author Heiko W. Rupp
* @author Ian Springer
*/
public class CacheConfigurationHelper {
private Log log = LogFactory.getLog(CacheConfigurationHelper.class);
/**
* Write a configuration to the cache
* @param file File to write to
* @param config The configuration to write
* @param mbeanName The name of the cache mbean in the file
* @param isUpdate Are we updating a file or creating a new one
* @throws JDOMException
* @throws IOException
*/
public void writeConfig(File file, Configuration config, String mbeanName, boolean isUpdate) throws JDOMException,
IOException {
Document doc;
Element root;
Element cacheMbean;
String flavour = config.getSimple("Flavour").getStringValue();
boolean isTc = false;
if (flavour != null && flavour.startsWith("tree")) {
isTc = true;
}
if (isUpdate) {
if (!file.exists() || !file.canWrite()) {
throw new IllegalStateException("Can't update file, as it is not writeable.");
}
SAXBuilder builder = new SAXBuilder();
SelectiveSkippingEntityResolver entityResolver = SelectiveSkippingEntityResolver.getDtdAndXsdSkippingInstance();
builder.setEntityResolver(entityResolver);
doc = builder.build(file);
root = doc.getRootElement();
cacheMbean = findComponentElement(root, mbeanName);
if (cacheMbean == null) {
throw new IllegalStateException("File does not contain an MBean with name '" + mbeanName + "'.");
}
} else { // new file
doc = new Document();
root = new Element("server");
doc.setRootElement(root);
cacheMbean = new Element("mbean");
cacheMbean.setAttribute("name", mbeanName);
root.addContent(cacheMbean);
}
if (isTc) {
cacheMbean.setAttribute("code", "org.jboss.cache.TreeCache");
} else {
cacheMbean.setAttribute("code", "org.jboss.cache.PojoCache");
}
// Make sure 'depends' elements exist for the cache service's service dependencies.
addJBossServiceDependsIfNotPresent(cacheMbean, "jboss:service=Naming");
addJBossServiceDependsIfNotPresent(cacheMbean, "jboss:service=TransactionManager");
// Add or update 'attribute' elements for each of the config props.
for (String propName : config.getSimpleProperties().keySet()) {
if (propName.equals("Flavour")) {
// Skip this prop, which we already used above to set the mbean class.
continue;
}
String propVal = config.getSimple(propName).getStringValue();
Element attribute = null;
if (isUpdate) { // on update we need to find the existing element and update it
attribute = findAttributeNodeWithName(cacheMbean, propName);
}
if (propVal == null) {
// for a null value, delete the 'attribute' element if one exists to tell the mbean to use its internal
// default value for that attribute
if (attribute != null) {
cacheMbean.removeContent(attribute);
}
} else {
if (attribute == null) {
// an 'attribute' element for this attribute does not yet exist - add one.
attribute = new Element("attribute");
attribute.setAttribute("name", propName);
cacheMbean.addContent(attribute);
}
attribute.setText(propVal);
}
}
// TODO add the cluster config
// now write the changes to disk
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
outputter.output(doc, fos);
fos.flush();
} catch (IOException ioe) {
log.error("Can't write the config : " + ioe);
} finally {
if (fos != null) {
fos.close();
}
}
}
private void addJBossServiceDependsIfNotPresent(Element mBeanElement, String serviceMBeanName) {
boolean foundDepends = false;
List dependsElements = mBeanElement.getChildren("depends");
for (Object dependsObj : dependsElements) {
Element depends = (Element) dependsObj;
if (depends.getText().equals(serviceMBeanName)) {
foundDepends = true;
break;
}
}
if (!foundDepends) {
Element depends = new Element("depends");
depends.setText(serviceMBeanName);
mBeanElement.addContent(depends);
}
}
/**
* Find the <attribute name="XX"> node below base, where XX matches the input.
* If no node is found, null is returned.
* @param base The base element under which to search
* @param propName The name attribute for which to search
* @return The element found or null
*/
private Element findAttributeNodeWithName(Element base, String propName) {
if (propName == null) {
return null;
}
for (Object attrObj : base.getChildren("attribute")) {
if (attrObj instanceof Element) {
Element attr = (Element) attrObj;
String nameAttrib = attr.getAttributeValue("name");
if (propName.equals(nameAttrib)) {
return attr;
}
}
}
return null;
}
private Element findComponentElement(Element base, String mbeanName) {
for (Object mbeanObj : base.getChildren("mbean")) {
if (mbeanObj instanceof Element) {
Element mbean = (Element) mbeanObj;
// normalize the content of 'name'
String nameAttrib = mbean.getAttributeValue("name");
try {
ObjectName on = new ObjectName(nameAttrib);
nameAttrib = on.getCanonicalName();
} catch (MalformedObjectNameException e) {
log.warn("Can't canonicalize " + nameAttrib);
}
if (nameAttrib.equals(mbeanName))
return mbean;
}
}
return null;
}
}