/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2007-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) 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 OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.netmgt.ticketer.quickbase;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.io.IOUtils;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.api.integration.ticketing.Ticket;
import org.opennms.api.integration.ticketing.Plugin;
import org.opennms.api.integration.ticketing.Ticket.State;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.util.Assert;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.intuit.quickbase.util.QuickBaseClient;
import com.intuit.quickbase.util.QuickBaseException;
/**
* OpenNMS Trouble Ticket Plugin API implementation for Intuit's QuickBase Trouble Ticketing.
*
* @author <a href="mailto:brozow@opennms.org">Mathew Brozowski</a>
* @author <a href="mailto:david@opennms.org">David Hustace</a>
*
*/
public class QuickBaseTicketerPlugin implements Plugin {
Properties m_properties;
public Ticket get(String ticketId) {
try {
Properties props = getProperties();
MyQuickBaseClient qdb = createClient(getUserName(props), getPassword(props), getUrl(props));
String dbId = qdb.findDbByName(getApplicationName(props));
HashMap<String, String> record = qdb.getRecordInfo(dbId, ticketId);
Ticket ticket = new Ticket();
ticket.setId(ticketId);
ticket.setModificationTimestamp(record.get(getModificationTimeStampFile(props)));
ticket.setSummary(record.get(getSummaryField(props)));
ticket.setDetails(record.get(getDetailsField(props)));
ticket.setState(getTicketStateValue(record.get(getStateField(props)), props));
return ticket;
} catch (Throwable e) {
throw new DataRetrievalFailureException("Failed to commit QuickBase transaction: "+e.getMessage(), e);
}
}
private MyQuickBaseClient createClient(String username, String passwd, String url) {
return new MyQuickBaseClient(username, passwd, url);
}
private Properties getProperties() {
File home = new File(System.getProperty("opennms.home"));
File etc = new File(home, "etc");
File config = new File(etc, "quickbase.properties");
Properties props = new Properties(System.getProperties());
InputStream in = null;
try {
in = new FileInputStream(config);
props.load(in);
} catch (IOException e) {
log().error("Unable to load "+config+" ignoring.", e);
} finally {
IOUtils.closeQuietly(in);
}
return props;
}
/**
* Convenience logging.
* @return a log4j Category for this class
*/
private ThreadCategory log() {
return ThreadCategory.getInstance(getClass());
}
public void saveOrUpdate(Ticket ticket) {
try {
Properties props = getProperties();
QuickBaseClient qdb = createClient(getUserName(props), getPassword(props), getUrl(props));
String dbId = qdb.findDbByName(getApplicationName(props));
HashMap<String, String> record = new HashMap<String, String>();
record.put(getSummaryField(props), ticket.getSummary());
record.put(getDetailsField(props), ticket.getDetails());
record.put(getStateField(props), getQuickBaseStateValue(ticket.getState(), props));
if (ticket.getId() == null) {
addAdditionCreationFields(record, props);
String recordId = qdb.addRecord(dbId, record);
ticket.setId(recordId);
} else {
Ticket oldTicket = get(ticket.getId());
if (ticket.getModificationTimestamp().equals(oldTicket.getModificationTimestamp())) {
qdb.editRecord(dbId, record, ticket.getId());
} else {
throw new OptimisticLockingFailureException("Ticket has been updated while this ticket was in memory! Reload and try again!");
}
}
} catch (Throwable e) {
throw new DataRetrievalFailureException("Failed to commit QuickBase transaction: "+e.getMessage(), e);
}
}
private String getQuickBaseStateValue(State state, Properties props) {
return getRequiredProperty(props, "statemap.ticket."+state.name());
}
private State getTicketStateValue(String status, Properties props) {
return State.valueOf(getRequiredProperty(props, "statemap.quickbase."+status));
}
private String getRequiredProperty(Properties props, String propName) {
assertPropertyDefined(props, propName);
return props.getProperty(propName);
}
private void assertPropertyDefined(Properties props, String propName) {
Assert.notNull(props.getProperty(propName), propName + " is not defined in quickbase.properties");
}
private String getIdField(Properties props) {
return getRequiredProperty(props, "ticket.id");
}
private String getModificationTimeStampFile(Properties props) {
return getRequiredProperty(props, "ticket.modificationTimestamp");
}
private String getStateField(Properties props) {
return getRequiredProperty(props, "ticket.state");
}
private String getDetailsField(Properties props) {
return getRequiredProperty(props, "ticket.details");
}
private String getSummaryField(Properties props) {
return getRequiredProperty(props, "ticket.summary");
}
private String getApplicationName(Properties props) {
return getRequiredProperty(props, "quickbase.appname");
}
private String getUserName(Properties props) {
return getRequiredProperty(props, "quickbase.username");
}
private String getPassword(Properties props) {
return getRequiredProperty(props, "quickbase.password");
}
private String getUrl(Properties props) {
return getRequiredProperty(props, "quickbase.url");
}
private void addAdditionCreationFields(HashMap<String, String> record, Properties props) {
final String prefix = "quickbase.create.";
Enumeration keys = props.propertyNames();
while(keys.hasMoreElements()) {
String key = (String)keys.nextElement();
if (key.startsWith(prefix)) {
String field = key.substring(prefix.length());
record.put(field, props.getProperty(key));
}
}
}
private static class MyQuickBaseClient extends QuickBaseClient {
public MyQuickBaseClient(String username, String password, String url) {
super(username, password, url);
}
public HashMap<String,String> getRecordInfo(String dbid, String rid) throws QuickBaseException, Exception {
Document qdbRequest = newXmlDocument();
addRequestParameter(qdbRequest, "rid", rid);
Document qdbResponse = postApiXml(dbid, "API_GetRecordInfo", qdbRequest);
NodeList records = getNodeList(qdbResponse, "field");
if (records == null) return null;
HashMap<String,String> record = new HashMap<String,String>(0);
for (int recordCounter = 0; recordCounter < records.getLength(); recordCounter++){
Element field = (Element)records.item(recordCounter);
String id = field.getElementsByTagName("fid").item(0).getChildNodes().item(0).getNodeValue();
Node valueNode = field.getElementsByTagName("value").item(0).getChildNodes().item(0);
String value = (valueNode == null ? null : valueNode.getNodeValue());
record.put(id, value);
}
return record;
}
private NodeList getNodeList(Document xmlDoc, String select){
String currentNodeName;
Element el = xmlDoc.getDocumentElement();
NodeList nl = null;
StringTokenizer st = new StringTokenizer(select, "/");
while (st.hasMoreTokens()){
currentNodeName = st.nextToken();
if (el ==null){return null;}
nl = el.getElementsByTagName(currentNodeName);
el = (Element) nl.item(0);
}
return nl;
}
}
}