/******************************************************************************* * 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.centric; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Map; import java.util.Properties; import org.apache.commons.io.IOUtils; import org.aspcfs.apps.transfer.DataRecord; import org.aspcfs.utils.CRMConnection; import org.aspcfs.utils.XMLUtils; import org.opennms.core.utils.ThreadCategory; import org.springframework.dao.DataRetrievalFailureException; import org.springframework.util.Assert; import org.w3c.dom.Element; import org.opennms.api.integration.ticketing.Plugin; import org.opennms.api.integration.ticketing.Ticket; import org.opennms.api.integration.ticketing.Ticket.State; /** * OpenNMS Trouble Ticket Plugin API implementation for CentricCRM (c) Darkhorse Ventures. * * @author <a href="mailto:brozow@opennms.org">Mathew Brozowski</a> * @author <a href="mailto:david@opennms.org">David Hustace</a> * */ public class CentricTicketerPlugin implements Plugin { /** * This class extends Centric Class that is responsible for transferring data * to/from the Centric server via their HTTP-XML API. * * @author <a href="mailto:brozow@opennms.org">Mathew Brozowski</a> * @author <a href="mailto:david@opennms.org">David Hustace</a> * */ public static class CentricConnection extends CRMConnection { /** * Convenience method added to retrieve error message embedded in the XML * packet returned by the CentricCRM API. * * @return <code>java.lang.String</code> if an error message exists in the server response. */ public String getErrorText() throws CentricPluginException { XMLUtils xml; try { String responseXML = getLastResponse(); if (responseXML == null) { return "Connection failed. See output.log for details"; } xml = new XMLUtils(responseXML); Element response = xml.getFirstChild("response"); Element errorText = XMLUtils.getFirstChild(response, "errorText"); return errorText.getTextContent(); } catch (Throwable e) { throw new CentricPluginException(e); } } /** * Wrapper class used to nicely handle Centric API exceptions * * @author <a href="mailto:brozow@opennms.org">Mathew Brozowski</a> * @author <a href="mailto:david@opennms.org">David Hustace</a> * */ class CentricPluginException extends RuntimeException { private static final long serialVersionUID = -2279922257910422937L; public CentricPluginException(Throwable e) { super(e); } } } /** * Implementation of TicketerPlugin API call to retrieve a CentricCRM trouble ticket. * @return an OpenNMS */ public Ticket get(String ticketId) { CentricConnection crm = createConnection(); ArrayList<String> returnFields = new ArrayList<String>(); returnFields.add("id"); returnFields.add("modified"); returnFields.add("problem"); returnFields.add("comment"); returnFields.add("stateId"); crm.setTransactionMeta(returnFields); DataRecord query = new DataRecord(); query.setName("ticketList"); query.setAction(DataRecord.SELECT); query.addField("id", ticketId); boolean success = crm.load(query); if (!success) { throw new DataRetrievalFailureException(crm.getLastResponse()); } Ticket ticket = new Ticket(); ticket.setId(crm.getResponseValue("id")); ticket.setModificationTimestamp(crm.getResponseValue("modified")); ticket.setSummary(crm.getResponseValue("problem")); ticket.setDetails(crm.getResponseValue("comment")); ticket.setState(getStateFromId(crm.getResponseValue("stateId"))); return ticket; } /** * Convenience method of determining a "close" state of a ticket. * @param newState * @return true if canceled or closed state */ private boolean isClosingState(State newState) { switch(newState) { case CANCELLED: case CLOSED: return true; case OPEN: default: return false; } } /** * Convenience method for converting a string representation of * the OpenNMS enumerated ticket states. * * @param stateIdString * @return the converted <code>org.opennms.api.integration.ticketing.Ticket.State</code> */ private State getStateFromId(String stateIdString) { if (stateIdString == null) { return State.OPEN; } int stateId = Integer.parseInt(stateIdString); switch(stateId) { case 1: return State.OPEN; case 2: return State.OPEN; case 3: return State.OPEN; case 4: return State.OPEN; case 5: return State.CLOSED; case 6: return State.CANCELLED; case 7: return State.CANCELLED; default: return State.OPEN; } } /** * Helper method for creating a CentricCRM DataRecord from properties * defined in the centric.properties file. * * @return a populated <code>org.aspcfs.apps.transfer.DataRecord</code> */ private DataRecord createDataRecord() { DataRecord record = new DataRecord(); Properties props = getProperties(); for(Map.Entry<Object, Object> entry : props.entrySet()) { String key = (String)entry.getKey(); String val = (String)entry.getValue(); if (!key.startsWith("connection.")) { record.addField(key, val); } } return record; } /** * Retrieves the properties defined in the centric.properties file. * * @return a <code>java.util.Properties object containing centric plugin defined properties */ private Properties getProperties() { File home = new File(System.getProperty("opennms.home")); File etc = new File(home, "etc"); File config = new File(etc, "centric.properties"); Properties props = new Properties(); 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()); } /* * (non-Javadoc) * @see org.opennms.api.integration.ticketing.Plugin#saveOrUpdate(org.opennms.api.integration.ticketing.Ticket) */ public void saveOrUpdate(Ticket ticket) { CentricConnection crm = createConnection(); ArrayList<String> returnFields = new ArrayList<String>(); returnFields.add("id"); crm.setTransactionMeta(returnFields); DataRecord record = createDataRecord(); record.setName("ticket"); if (ticket.getId() == null) { record.setAction(DataRecord.INSERT); } else { record.setAction(DataRecord.UPDATE); record.addField("id", ticket.getId()); record.addField("modified", ticket.getModificationTimestamp()); } record.addField("problem", ticket.getSummary()); record.addField("comment", ticket.getDetails()); record.addField("stateId", getStateId(ticket.getState())); record.addField("closeNow", isClosingState(ticket.getState())); crm.save(record); boolean success = crm.commit(); if (!success) { throw new DataRetrievalFailureException("Failed to commit Centric transaction: "+crm.getErrorText()); } Assert.isTrue(1 == crm.getRecordCount(), "Unexpected record count from CRM"); String id = crm.getResponseValue("id"); ticket.setId(id); /* <map class="org.aspcfs.modules.troubletickets.base.Ticket" id="ticket"> <property alias="guid">id</property> <property lookup="account">orgId</property> <property lookup="contact">contactId</property> <property>problem</property> <property>entered</property> <property lookup="user">enteredBy</property> <property>modified</property> <property lookup="user">modifiedBy</property> <property>closed</property> <property lookup="ticketPriority">priorityCode</property> <property>levelCode</property> <property lookup="lookupDepartment">departmentCode</property> <property lookup="lookupTicketSource">sourceCode</property> <property lookup="ticketCategory">catCode</property> <property lookup="ticketCategory">subCat1</property> <property lookup="ticketCategory">subCat2</property> <property lookup="ticketCategory">subCat3</property> <property lookup="user">assignedTo</property> <property>comment</property> <property>solution</property> <property lookup="ticketSeverity">severityCode</property> <!-- REMOVE: critical --> <!-- REMOVE: notified --> <!-- REMOVE: custom_data --> <property>location</property> <property>assignedDate</property> <property>estimatedResolutionDate</property> <property>resolutionDate</property> <property>cause</property> <property>contractId</property> <property>assetId</property> <property>productId</property> <property>customerProductId</property> <property>expectation</property> <property>projectTicketCount</property> <property>estimatedResolutionDateTimeZone</property> <property>assignedDateTimeZone</property> <property>resolutionDateTimeZone</property> <property>statusId</property> <property>trashedDate</property> <property>userGroupId</property> <property>causeId</property> <property>resolutionId</property> <property>defectId</property> <property>escalationLevel</property> <property>resolvable</property> <property>resolvedBy</property> <property>resolvedByDeptCode</property> <property>stateId</property> <property>siteId</property> </map> */ } /* private String getModifiedTimestamp(String id) { CentricConnection crm = createConnection(); ArrayList<String> returnFields = new ArrayList<String>(); returnFields.add("id"); returnFields.add("modified"); crm.setTransactionMeta(returnFields); DataRecord query = new DataRecord(); query.setAction(DataRecord.SELECT); query.setName("ticketList"); query.addField("id", 91); crm.load(query); return crm.getResponseValue("modified"); } */ /** * Convenience method for converting OpenNMS Ticket.State enum * to an int representation compatible with CentricCRM. * * TODO: This needs to be configurable with the ability of the user * to define. */ private int getStateId(State state) { switch(state) { case OPEN: return 2; case CANCELLED: return 6; case CLOSED: return 5; default: return 2; } } /** * Creates connection to CentricCRM server using CentricCRM HTTP-XML API * @return a connection to the configured CentricCRM server. */ private CentricConnection createConnection() { // Client ID must already exist in target CRM system and is created // under Admin -> Configure System -> HTTP-XML API Client Manager Properties props = getProperties(); // Establish connectivity as a client CentricConnection crm = new CentricConnection(); crm.setUrl(props.getProperty("connection.url")); crm.setId(props.getProperty("connection.id")); crm.setCode(props.getProperty("connection.code")); crm.setClientId(props.getProperty("connection.clientId")); // Start a new transaction crm.setAutoCommit(false); return crm; } }