/*
* Jopr Management Platform
* Copyright (C) 2005-2008 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.jbossas.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
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.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.resource.CreateResourceStatus;
import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
import org.rhq.core.pluginapi.inventory.CreateResourceReport;
import org.rhq.core.pluginapi.util.SelectiveSkippingEntityResolver;
/**
* Loads and edits Connection Factories in the connection factory xml format
*
* @author Mark Spritzler
*/
public class ConnectionFactoryConfigurationEditor {
//private static final String NO_TX_TYPE = "no-tx-connection-factory";
private static final String XA_TX_TYPE = "tx-connection-factory";
private static final String[] COMMON_PROPS = { "jndi-name", "user-name", "password", "min-pool-size",
"max-pool-size", "adapter-display-name", "rar-name", "connection-definition", "track-connection-by-tx",
"use-java-context", "no-tx-separate-pools", "isSameRM-override-value", "use-strict-min", "depends", "prefill",
"blocking-timeout-millis", "type-mapping", "new-connection-sql", "check-valid-connection-sql",
"idle-timeout-minutes" };
private static final List<String> blankProps;
static {
blankProps = new ArrayList<String>();
blankProps.add("track-connection-by-tx");
}
private static final String[] XA_PROPS = { "xa-resource-timeout" };
private static final String APPLICATION_MANAGED_SECURITY = "application-managed-security";
private static final String SECURITY_AND_APPLICATION = "security-domain-and-application";
private static final String SECURITY_APP_MANAGED = "security-domain";
// Using a different constant for the same string, because the first two are for the elements tags
// and this one below is for a Resource Configuration Property.
private static final String SECURITY_DOMAIN = "security-domain";
//private static final String DEPENDS_PROPERTY = "depends";
private static final String TRANSACTION_TYPE_PROPERTY = "transaction-type";
private static final String LOCAL_TX_TYPE_PROPERTY_VALUE = "Local Transaction";
private static final String XA_TX_TYPE_PROPERTY_VALUE = "XA Transaction";
private static final String CONFIG_PROPERTY = "config-property";
private static final String CONFIG_PROPERTY_NAME = "config-property-name";
private static final String CONFIG_PROPERTY_TYPE = "config-property-type";
private static final String CONFIG_PROPERTY_VALUE = "config-property-value";
private static Log log = LogFactory.getLog(ConnectionFactoryConfigurationEditor.class);
public static Configuration loadConnectionFactory(File file, String name) {
try {
SAXBuilder builder = new SAXBuilder();
SelectiveSkippingEntityResolver entityResolver = SelectiveSkippingEntityResolver.getDtdAndXsdSkippingInstance();
builder.setEntityResolver(entityResolver);
Document doc = builder.build(file);
// Get the root element
Element root = doc.getRootElement();
if (!root.getName().equals("connection-factories")) {
return null;
}
Element connectionFactoryElement = findConnectionFactoryElement(root, name);
if (connectionFactoryElement == null) {
return null;
}
Configuration config = new Configuration();
String type = connectionFactoryElement.getName();
config.put(new PropertySimple("type", type));
bindElements(connectionFactoryElement, config, COMMON_PROPS);
bindSecurityInfo(connectionFactoryElement, config);
bindTransactionType(connectionFactoryElement, config);
if (type.equals(XA_TX_TYPE)) {
bindElements(connectionFactoryElement, config, XA_PROPS);
}
bindList(connectionFactoryElement, config, CONFIG_PROPERTY);
//bindList(connectionFactoryElement, config, DEPENDS_PROPERTY);
return config;
} catch (IOException e) {
log.error("IO error occurred while reading file: " + file, e);
} catch (JDOMException e) {
log.error("Parsing error occurred while reading file: " + file, e);
}
return null;
}
/**
* Writes out connection factory changes to the file system. If the file does not exist it will create a new file in the
* requested location.
*
* @param deploymentFile XML File that holds the Connection Factory information
* @param name Name of the Connection Factory
* @param report ConfigurationUpdateReport object
*/
public static void updateConnectionFactory(File deploymentFile, String name, ConfigurationUpdateReport report) {
try {
updateConnectionFactory(deploymentFile, name, report.getConfiguration());
report.setStatus(ConfigurationUpdateStatus.SUCCESS);
} catch (IOException e) {
report.setErrorMessageFromThrowable(e);
log.error("IO error occurred while updating connection factory at file: " + deploymentFile, e);
} catch (JDOMException e) {
report.setErrorMessageFromThrowable(e);
log.error("Parsing error occurred while updating connection factory at file: " + deploymentFile, e);
} catch (Exception e) {
report.setErrorMessageFromThrowable(e);
log.error("Unable to update connection factory at file" + deploymentFile, e);
}
}
public static void updateConnectionFactory(File deploymentFile, String name, CreateResourceReport report) {
try {
updateConnectionFactory(deploymentFile, name, report.getResourceConfiguration());
report.setStatus(CreateResourceStatus.SUCCESS);
} catch (IOException e) {
report.setException(e);
log.error("IO error occurred while creating connection factory at file: " + deploymentFile, e);
} catch (JDOMException e) {
report.setException(e);
log.error("Parsing error occurred while creating connection factory at file: " + deploymentFile, e);
} catch (Exception e) {
report.setException(e);
log.error("Unable to create connection factory at file" + deploymentFile, e);
}
}
private static void updateConnectionFactory(File deploymentFile, String name, Configuration config)
throws JDOMException, IOException {
Document doc;
Element root;
if (deploymentFile.exists()) {
SAXBuilder builder = new SAXBuilder();
SelectiveSkippingEntityResolver entityResolver = SelectiveSkippingEntityResolver.getDtdAndXsdSkippingInstance();
builder.setEntityResolver(entityResolver);
doc = builder.build(deploymentFile);
root = doc.getRootElement();
} else {
doc = new Document();
root = new Element("connection-factories");
doc.setRootElement(root);
}
if (!root.getName().equals("connection-factories")) {
throw new RuntimeException("Connection Factory file format exception on [" + deploymentFile
+ "], expected [connection-factories] element but found [" + root.getName() + "]");
}
Element connectionFactoryElement = findConnectionFactoryElement(root, name);
String type = config.getSimpleValue("type", null);
boolean isNewConnectionFactory = false;
if (connectionFactoryElement == null) {
connectionFactoryElement = new Element(type);
isNewConnectionFactory = true;
} else if (!type.equals(connectionFactoryElement.getName())) {
connectionFactoryElement.setName(type);
}
updateElements(connectionFactoryElement, config, COMMON_PROPS);
updateSecurityInfo(connectionFactoryElement, config);
updateTransactionType(connectionFactoryElement, config);
if (type.equals(XA_TX_TYPE)) {
updateElements(connectionFactoryElement, config, XA_PROPS);
}
updateList(connectionFactoryElement, config, CONFIG_PROPERTY);
//updateList(connectionFactoryElement, config, DEPENDS_PROPERTY);
if (isNewConnectionFactory) {
root.addContent(connectionFactoryElement);
}
updateFile(deploymentFile, doc);
}
public static void deleteConnectionFactory(File deploymentFile, String name) {
Document doc;
Element root;
if (deploymentFile != null && deploymentFile.exists()) {
try {
SAXBuilder builder = new SAXBuilder();
SelectiveSkippingEntityResolver entityResolver = SelectiveSkippingEntityResolver.getDtdAndXsdSkippingInstance();
builder.setEntityResolver(entityResolver);
doc = builder.build(deploymentFile);
root = doc.getRootElement();
if (root != null) {
if (!root.getName().equals("connection-factories")) {
throw new RuntimeException("Connection Factory file format exception on [" + deploymentFile
+ "], expected [connection-factories] element but found [" + root.getName() + "]");
}
Element connectionFactoryElement = findConnectionFactoryElement(root, name);
root.removeContent(connectionFactoryElement);
}
updateFile(deploymentFile, doc);
} catch (JDOMException e) {
log.error("Parsing error occurred while deleting connection factory at file: " + deploymentFile, e);
} catch (IOException e) {
log.error("IO error occurred while deleting connection factory at file: " + deploymentFile, e);
}
} else {
// Not able to get to the deploymentFile
}
}
/*private static void updateListOfMaps(Element parent, Configuration configuration, String name) {
PropertyList propertyList = configuration.getList(name);
List elementList = parent.getChildren(name);
if (!elementList.isEmpty()) {
parent.removeChildren(name);
}
for (Property prop : propertyList.getList()) {
Element element = new Element(name);
PropertyMap propertyMap = (PropertyMap) prop;
PropertySimple configPropertyName = (PropertySimple) propertyMap.getMap().get(CONFIG_PROPERTY_NAME);
PropertySimple configPropertyType = (PropertySimple) propertyMap.getMap().get(CONFIG_PROPERTY_TYPE);
PropertySimple configPropertyValue = (PropertySimple) propertyMap.getMap().get(CONFIG_PROPERTY_VALUE);
element.setAttribute("name", configPropertyName.getStringValue());
element.setAttribute("type", configPropertyType.getStringValue());
element.setText(configPropertyValue.getStringValue());
elementList.add(element);
parent.addContent(elementList);
}
}*/
private static void updateList(Element parent, Configuration config, String propertyName) {
PropertyList propertyList = config.getList(propertyName);
List elementList = parent.getChildren(propertyName);
if (!elementList.isEmpty()) {
parent.removeChildren(propertyName);
}
for (Property property : propertyList.getList()) {
Element element = new Element(propertyName);
if (propertyName.equals(CONFIG_PROPERTY)) {
setListElementValuesForConfigProperties(property, element);
} else {
setListElementValuesForDepends(property, element);
}
parent.addContent(element);
}
}
private static void setListElementValuesForConfigProperties(Property property, Element element) {
PropertyMap propertyMap = (PropertyMap) property;
PropertySimple configPropertyName = (PropertySimple) propertyMap.getMap().get(CONFIG_PROPERTY_NAME);
PropertySimple configPropertyType = (PropertySimple) propertyMap.getMap().get(CONFIG_PROPERTY_TYPE);
PropertySimple configPropertyValue = (PropertySimple) propertyMap.getMap().get(CONFIG_PROPERTY_VALUE);
element.setAttribute("name", configPropertyName.getStringValue());
element.setAttribute("type", configPropertyType.getStringValue());
element.setText(configPropertyValue.getStringValue());
}
public static void setListElementValuesForDepends(Property property, Element element) {
PropertySimple propertySimple = (PropertySimple) property;
element.setText(propertySimple.getStringValue());
}
private static void updateElements(Element parent, Configuration config, String[] names) {
for (String prop : names) {
updateElement(parent, config, prop);
}
}
private static void updateElement(Element parent, Configuration config, String name) {
String value = config.getSimpleValue(name, null);
Element child = parent.getChild(name);
if (value == null || value.equals("")) {
if (child != null) {
parent.removeContent(child);
}
} else {
if (child == null) {
child = new Element(name);
parent.addContent(child);
}
// If this is a blank boolean property, we do not want a value in the tag, just the tag itself
if (blankProps.contains(name)) {
if (value.equals("false")) {
parent.removeContent(child);
}
} else {
child.setText(value);
}
}
}
/*
* APPLICATION_MANAGED_SECURITY and SECURITY_APP_MANAGED are combined, you have to have both.
* If you want a Security Domain that is not App Managed then you just need the SECURITY_AND_APPLICATION tags
*/
private static void updateSecurityInfo(Element parent, Configuration config) {
// In Jon's configurion SecurityDomain and SecurityDomainAndApplication is stored in the same property
String appManagedValue = config.getSimpleValue(APPLICATION_MANAGED_SECURITY, null);
String securityDomainValue = config.getSimpleValue(SECURITY_DOMAIN, null);
Element useApplication = parent.getChild(APPLICATION_MANAGED_SECURITY);
Element securityDomain = parent.getChild(SECURITY_APP_MANAGED);
Element securityAndApp = parent.getChild(SECURITY_AND_APPLICATION);
// App Managed means you need both the Application Managed Security and Security Domain tags in the xml
if (appManagedValue == null) {
// First make sure the APPLICATION_MANAGED_SECURITY and SECURITY_APP_MANAGED are not elements
if (useApplication != null) {
parent.removeContent(useApplication);
}
if (securityDomain != null) {
parent.removeContent(securityDomain);
}
// If this is not app managed, and there is a value in the securityAndAppProperty then need the
// security-domain element.
if (securityDomainValue != null && !securityDomainValue.equals("")) {
if (securityAndApp == null) {
securityAndApp = new Element(SECURITY_AND_APPLICATION);
parent.addContent(securityAndApp);
}
securityAndApp.setText(securityDomainValue);
}
} else {
// If missing the security-domain information while being App Managed should cause the update to fail.
if (securityDomainValue == null || securityDomainValue.equals("")) {
log.error("Security Domain property is required if selecting to use Application Managed Security");
throw new RuntimeException(
"Security Domain property is required if selecting to use Application Managed Security");
} else {
if (useApplication == null) {
useApplication = new Element(APPLICATION_MANAGED_SECURITY);
parent.addContent(useApplication);
}
// There should not be a security-domain-and-application element, if there is remove it
if (securityAndApp != null) {
parent.removeContent(securityAndApp);
}
if (securityDomain == null) {
securityDomain = new Element(SECURITY_APP_MANAGED);
}
securityDomain.setText(securityDomainValue);
}
}
}
private static void updateTransactionType(Element parent, Configuration config) {
String transactionType = config.getSimpleValue(TRANSACTION_TYPE_PROPERTY, null);
Element useLocalTransactionType = parent.getChild("local-transaction");
Element useXATransactionType = parent.getChild("xa-transaction");
// delete any existing elements tags
if (useLocalTransactionType != null) {
parent.removeChild("local-transaction");
}
if (useXATransactionType != null) {
parent.removeChild("xa-transaction");
}
// if a transactionType was specified add the appropriate element back
if (transactionType != null) {
if (transactionType.equals(LOCAL_TX_TYPE_PROPERTY_VALUE)) {
Element child = new Element("local-transaction");
parent.addContent(child);
} else {
Element child = new Element("xa-transaction");
parent.addContent(child);
}
}
}
/*
private static void bindListOfMaps(Element parent, Configuration config, String listName) {
PropertyList listOfMaps = new PropertyList(listName);
List<Property> maps = new ArrayList<Property>();
for (Object child : parent.getChildren(listName)) {
Element childElement = (Element) child;
String name = childElement.getAttributeValue("name");
String type = childElement.getAttributeValue("type");
String value = childElement.getText();
PropertyMap map = new PropertyMap("property-values");
map.put(new PropertySimple(CONFIG_PROPERTY_NAME, name));
map.put(new PropertySimple(CONFIG_PROPERTY_TYPE, type));
map.put(new PropertySimple(CONFIG_PROPERTY_VALUE, value));
maps.add(map);
}
listOfMaps.setList(maps);
config.put(listOfMaps);
}
*/
private static void bindList(Element parent, Configuration config, String listName) {
PropertyList propertyList = new PropertyList(listName);
List<Property> properties = new ArrayList<Property>();
for (Object child : parent.getChildren(listName)) {
Element childElement = (Element) child;
if (listName.equals(CONFIG_PROPERTY)) {
setListOfConfigProperties(properties, childElement);
} else {
setListOfDependencyProperties(properties, childElement);
}
}
propertyList.setList(properties);
config.put(propertyList);
}
private static void setListOfConfigProperties(List<Property> propertyList, Element element) {
String name = element.getAttributeValue("name");
String type = element.getAttributeValue("type");
String value = element.getText();
PropertyMap map = new PropertyMap("property-values");
map.put(new PropertySimple(CONFIG_PROPERTY_NAME, name));
map.put(new PropertySimple(CONFIG_PROPERTY_TYPE, type));
map.put(new PropertySimple(CONFIG_PROPERTY_VALUE, value));
propertyList.add(map);
}
private static void setListOfDependencyProperties(List<Property> propertyList, Element element) {
//String depends = element.getText();
//PropertySimple propertySimple = new PropertySimple(DEPENDS_PROPERTY, depends);
//propertyList.add(propertySimple);
}
private static void bindElements(Element parent, Configuration config, String[] names) {
for (String prop : names) {
bindElement(parent, config, prop);
}
}
private static void bindElement(Element parent, Configuration config, String name) {
Element child = parent.getChild(name);
if (child != null) {
// If this is a blank boolean property, we do not want a value in the tag, just the tag itself
if (!blankProps.contains(name)) {
config.put(new PropertySimple(name, child.getText()));
} else {
config.put(new PropertySimple(name, "true"));
}
}
}
private static void bindSecurityInfo(Element parent, Configuration config) {
Element useApplication = parent.getChild(APPLICATION_MANAGED_SECURITY);
if (useApplication != null) {
config.put(new PropertySimple(APPLICATION_MANAGED_SECURITY, "true"));
Element securityDomain = parent.getChild(SECURITY_APP_MANAGED);
if (securityDomain != null) {
config.put(new PropertySimple(SECURITY_DOMAIN, securityDomain.getValue()));
} else {
config.put(new PropertySimple(SECURITY_DOMAIN, "Value required with App Managed Security"));
}
} else {
Element domainAndApplication = parent.getChild(SECURITY_AND_APPLICATION);
if (domainAndApplication != null) {
config.put(new PropertySimple(SECURITY_DOMAIN, domainAndApplication.getValue()));
}
}
}
private static void bindTransactionType(Element parent, Configuration config) {
Element useLocalTransactionType = parent.getChild("local-transaction");
Element useXATransactionType = parent.getChild("xa-transaction");
if ((useXATransactionType != null && useLocalTransactionType != null)
|| ((useXATransactionType == null && useLocalTransactionType == null))) {
// This is an either/or
return;
}
if (useLocalTransactionType != null) {
config.put(new PropertySimple(TRANSACTION_TYPE_PROPERTY, LOCAL_TX_TYPE_PROPERTY_VALUE));
}
if (useXATransactionType != null) {
config.put(new PropertySimple(TRANSACTION_TYPE_PROPERTY, XA_TX_TYPE_PROPERTY_VALUE));
}
}
private static Element findConnectionFactoryElement(Element root, String name) {
for (Object child : root.getChildren()) {
Element childElement = (Element) child;
String jndiName = childElement.getChildText("jndi-name");
if (name.equals(jndiName)) {
return childElement;
}
}
return null;
}
private static void updateFile(File deploymentFile, Document doc) throws JDOMException, IOException {
FileOutputStream fos = new FileOutputStream(deploymentFile);
XMLOutputter outp = new XMLOutputter(Format.getPrettyFormat());
outp.output(doc, fos);
fos.flush();
fos.close();
}
}