From b9eb1329a6dbec7b75c21d8e0eb13134121db6bb Mon Sep 17 00:00:00 2001 From: Jesse Morgan Date: Sat, 19 Mar 2016 02:05:33 -0700 Subject: Initial commit for the CCB API Client. The client currently supports the following APIs: * individual_profiles * individual_profile_from_id * individual_profile_from_login_password * individual_profile_from_micr * custom_field_labels --- .../java/com/p4square/ccbapi/CCBXmlBinder.java | 78 ++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/main/java/com/p4square/ccbapi/CCBXmlBinder.java (limited to 'src/main/java/com/p4square/ccbapi/CCBXmlBinder.java') diff --git a/src/main/java/com/p4square/ccbapi/CCBXmlBinder.java b/src/main/java/com/p4square/ccbapi/CCBXmlBinder.java new file mode 100644 index 0000000..f347643 --- /dev/null +++ b/src/main/java/com/p4square/ccbapi/CCBXmlBinder.java @@ -0,0 +1,78 @@ +package com.p4square.ccbapi; + +import com.p4square.ccbapi.exception.CCBParseException; +import com.p4square.ccbapi.model.CCBAPIResponse; + +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import java.io.InputStream; +import java.util.concurrent.ConcurrentHashMap; + +/** + * CCBXmlBinder is used to bind XML responses to CCBAPIResponse implementations. + * + * This class is thread-safe. + */ +public class CCBXmlBinder { + private final XMLInputFactory xmlInputFactory; + private final ConcurrentHashMap, JAXBContext> jaxbContextCache; + + public CCBXmlBinder() { + xmlInputFactory = XMLInputFactory.newFactory(); + jaxbContextCache = new ConcurrentHashMap<>(); + } + + public T bindResponseXML(InputStream response, Class responseClass) + throws CCBParseException { + try { + final XMLStreamReader xmlReader = xmlInputFactory.createXMLStreamReader(response); + final JAXBContext jaxbContext = getJAXBContext(responseClass); + final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + + // Skip ahead to the response entity. + while (xmlReader.hasNext()) { + xmlReader.next(); + if (xmlReader.isStartElement() && "response".equalsIgnoreCase(xmlReader.getLocalName())) { + // Parse and return the response. + // If the response cannot be parsed a JAXBException will be thrown. + return (T) unmarshaller.unmarshal(xmlReader); + } + } + + // If we reach this point then the response did not contain a response element. + throw new CCBParseException("Response did not contain a response element."); + + } catch (XMLStreamException | JAXBException e) { + throw new CCBParseException("Failed to parse response.", e); + } + } + + /** + * Find or create the JAXBContext for a CCBAPIResponse implementation. + * + * @param responseClass The response implementation class. + * @return a JAXBContext which can be used to unmarshell the responseClass. + */ + private JAXBContext getJAXBContext(Class responseClass) { + if (!jaxbContextCache.containsKey(responseClass)) { + synchronized (jaxbContextCache) { + // Check again to be sure. + if (!jaxbContextCache.containsKey(responseClass)) { + try { + final JAXBContext jaxbContext = JAXBContext.newInstance(responseClass); + jaxbContextCache.put(responseClass, jaxbContext); + } catch (JAXBException e) { + throw new AssertionError("Could not construct JAXBContext for " + responseClass.getName(), e); + } + } + } + } + return jaxbContextCache.get(responseClass); + } +} -- cgit v1.2.3