Monday, August 02, 2010

Bean properties printer - a useful debugging tool

There are many instances when I wanted to just log the properties of a given object (most of the time its a bean). This will be useful in two ways: 1) learn about the concrete type of the object 2) serves as a good learning aid to understand what are all the properties that can be get/set in that object. Of course, if you have the documentation and the source code for the class in question, that would be the ultimate aid.

Nevertheless, the following piece of code would be useful to print the properties of the given object/bean, using the getter methods that are available in the object.
    public static void printProperties(String msg, Object o) {
        String className = o.getClass().getName();
        if(msg != null && msg.length() > 0)
  "{} (type {})", msg, className);
        Method[] methods = o.getClass().getMethods();
        for(Method m:methods) {
            if(m.getName().startsWith("get") && m.getParameterTypes().length == 0) {
                try {
                    Object returnValue = m.invoke(o);
          "  {} -> {} (type {})", toObjArr(m.getName(), returnValue,
                } catch (Exception e) {
                    logger.error("Exception thrown while invoking [{}]: {}", m.getName(), e);
You can view the source code of toObjArr() here. This method can be improved a bit. For e.g. methods starting with isXXX are not invoked in the code. Also, a recursive way of printing the properties (with an optional max depth) can be provided.

A sample output could look like this:
Contents of the SAML request (type org.opensaml.saml2.core.impl.AuthnRequestImpl)
   getSubject -> null (type org.opensaml.saml2.core.Subject)
   getOrderedChildren -> [org.opensaml.saml2.core.impl.IssuerImpl@15e14d9, org.opensaml.saml2.core.impl.ScopingImpl@1aacada] (type java.util.List)
   getConditions -> null (type org.opensaml.saml2.core.Conditions)
   getRequestedAuthnContext -> null (type org.opensaml.saml2.core.RequestedAuthnContext)
   getProtocolBinding -> urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST (type java.lang.String)
   getAssertionConsumerServiceIndex -> null (type java.lang.Integer)
   getAssertionConsumerServiceURL -> http://localhost:8080/mySP/resources/saml/SSO (type java.lang.String)
   getAttributeConsumingServiceIndex -> null (type java.lang.Integer)
   getProviderName -> null (type java.lang.String)
   getNameIDPolicy -> null (type org.opensaml.saml2.core.NameIDPolicy)
   getScoping -> org.opensaml.saml2.core.impl.ScopingImpl@1aacada (type org.opensaml.saml2.core.Scoping)
   getVersion -> 2.0 (type org.opensaml.common.SAMLVersion)
   getID -> a3hj688f209c170h433b6eh0h73e0j2 (type java.lang.String)
   getDestination -> http://localhost:8080/myapp/profile/SAML2/POST/SSO (type java.lang.String)
   getIssuer -> org.opensaml.saml2.core.impl.IssuerImpl@15e14d9 (type org.opensaml.saml2.core.Issuer)
   getIssueInstant -> 2010-08-02T18:37:33.528Z (type org.joda.time.DateTime)
   getSignatureReferenceID -> a3hj688f209c170h433b6eh0h73e0j2 (type java.lang.String)
   getConsent -> null (type java.lang.String)
   getExtensions -> null (type org.opensaml.saml2.common.Extensions)
   getValidators -> null (type java.util.List)
   getSignature -> null (type org.opensaml.xml.signature.Signature)
   getParent -> null (type org.opensaml.xml.XMLObject)
   getDOM -> null (type org.w3c.dom.Element)
   getSchemaType -> null (type javax.xml.namespace.QName)
   getElementQName -> {urn:oasis:names:tc:SAML:2.0:protocol}AuthnRequest (type javax.xml.namespace.QName)
   getIDIndex -> org.opensaml.xml.util.IDIndex@1ab000c (type org.opensaml.xml.util.IDIndex)
   getNamespaces -> [xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"] (type java.util.Set)
   getNoNamespaceSchemaLocation -> null (type java.lang.String)
   getSchemaLocation -> null (type java.lang.String)
   getClass -> class org.opensaml.saml2.core.impl.AuthnRequestImpl (type java.lang.Class)

Hope this serves as a useful tool in your troubleshooting/debugging.

No comments: