diff options
author | Jesse Morgan <jesse@jesterpm.net> | 2016-03-19 02:05:33 -0700 |
---|---|---|
committer | Jesse Morgan <jesse@jesterpm.net> | 2016-03-19 02:07:24 -0700 |
commit | b9eb1329a6dbec7b75c21d8e0eb13134121db6bb (patch) | |
tree | fec73ab32ff625c304513c24e864809845eede1a /src/main/java/com/p4square/ccbapi/CCBXmlBinder.java |
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
Diffstat (limited to 'src/main/java/com/p4square/ccbapi/CCBXmlBinder.java')
-rw-r--r-- | src/main/java/com/p4square/ccbapi/CCBXmlBinder.java | 78 |
1 files changed, 78 insertions, 0 deletions
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<Class<? extends CCBAPIResponse>, JAXBContext> jaxbContextCache; + + public CCBXmlBinder() { + xmlInputFactory = XMLInputFactory.newFactory(); + jaxbContextCache = new ConcurrentHashMap<>(); + } + + public <T extends CCBAPIResponse> T bindResponseXML(InputStream response, Class<T> 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<? extends CCBAPIResponse> 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); + } +} |