The Daily Flashback: The String in, String out API
January 8, 2011 2 Comments
I just ran across a recent Daily WTF story “XML’d XML” in my news feed. The gist is that some web service call which inherently has XML as return data returned a “string” as its payload. And it was a string, but a string of XML.
I instantly flashed back on an integration project from a few years back that we did for a Fortune 500 company. The company had contracted with a 3rd party service provider that provided a SOAP API for all of its message passing and we thought this would be much better than something out of the 1990s like passing PGP-encrypted CSV files over FTP. We were working with an—even at the time—ancient Sun ONE 6.0 server platform but it was able to support SOAP web services using Apache Axis.
I became concerned when I realized that every method took a single string “input” argument and returned a string.
I just pulled a sample from our source repository for old time sake.
/** * SubscriberAPISoap_PortType.java * * This file was auto-generated from WSDL * by the Apache Axis 1.4 Apr 22, 2006 (06:55:48 PDT) WSDL2Java emitter. */ package com.shallRemainNameless; import javax.xml.soap.SOAPException; public interface SubscriberAPISoap extends java.rmi.Remote, SoapAuthentication { public java.lang.String createSubscriber(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String updateSubscriber(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String addCategory(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String removeCategory(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String getSubscriber(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String getSubscriberData(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String authenticateSubscriber(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String getSubscribers(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String getSubscribersByField(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String deactivateSubscriber(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String activateSubscriber(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String sendOptInMessage(java.lang.String input) throws java.rmi.RemoteException; public java.lang.String getRegistrationMetaData(java.lang.String input) throws java.rmi.RemoteException; }
Every one of those input and return strings was actually XML. I remember when I realized it was XML all the way down, I sent an internal email with the subject “String in, String out == [expletive-deleted]”.
We created a wrapper API to hide the insanity but the actual XML API wasn’t so much better than the String version. They had a unified ApiResultDocument XML schema that encoded any possible result and an ApiRequestDocument schema which encoded any possible combination of arguments across all web methods.
/* * SubscriberAPISoapDocument.java * * Created on December 4, 2006, 2:59 PM * */ package com.shallRemainNameless.service; /** * * @author breiter */ import com.shallRemainNameless.service.schemas.apiResult.ApiResultDocument; import java.rmi.RemoteException; import javax.xml.soap.SOAPException; import org.apache.xmlbeans.XmlException; public interface SubscriberAPISoapStrong extends java.rmi.Remote, SoapAuthentication { public ApiResultDocument createSubscriber(com.shallRemainNameless.service.schemas.subscriberCreateAndUpdate.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument updateSubscriber(com.shallRemainNameless.service.schemas.subscriberCreateAndUpdate.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument addCategory(com.shallRemainNameless.service.schemas.addRemoveCategory.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument removeCategory(com.shallRemainNameless.service.schemas.addRemoveCategory.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument getSubscriber(com.shallRemainNameless.service.schemas.getSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument getSubscriberData(com.shallRemainNameless.service.schemas.getSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument authenticateSubscriber(com.shallRemainNameless.service.schemas.authenticateSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument getSubscribers(com.shallRemainNameless.service.schemas.getSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument getSubscribersByField(com.shallRemainNameless.service.schemas.getSubscribersByField.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument deactivateSubscriber(com.shallRemainNameless.service.schemas.deactivateSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument activateSubscriber(com.shallRemainNameless.service.schemas.activateSubscriber.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument sendOptInMessage(com.shallRemainNameless.service.schemas.sendOptInMessage.ApiRequestDocument xmldoc) throws RemoteException, XmlException; public ApiResultDocument getRegistrationMetaData(com.shallRemainNameless.service.schemas.getSubscribers.ApiRequestDocument xmldoc) throws RemoteException, XmlException; }
I remember that things got interesting when we created unit tests to confirm that the SOAP API did what it was supposed to do. There are 15 versions of unit tests checked in as we established that the documentation provided was not, in fact, based on the actual behavior of the system.
At least the designer of this API didn’t realize s/he could have just added an <ApiMethodRequest /> element to the ApiRequestDocument and used a single ApiResultDocument doApiRequest( ApiRequestDocument ) method. Oh wait, it would have been String doApiRequest( String ). That would have been epic.
Good times.
The real WTF is “java.lang.String” and friends. What ever happened to the import statement?
Did you notice that the class was machine generated by the Apache Axis WSDL compiler?