Add toString() methods at least to DTO classes

Hey guys.

I regularly miss toString-Methods in our code base. I think for DTO-alike classes (today I stumbled over https://github.com/camunda/camunda-bpm-platform/blob/master/engine/src/main/java/org/camunda/bpm/engine/impl/externaltask/LockedExternalTaskImpl.java) this should be standard to have them. What do you think?

Maybe you could even setup a rule to get warned by the IDE if they are missing? This is a one minute thing during coding, but really annoying when missing later and you want to log certain things easily… However - not a huge issue anyway (that’s why I address this via the forum only :-)).

My two cents…
Cheers
Bernd

1 Like

Hi Bernd,

Here’s a quick way to help yourself: https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/builder/ReflectionToStringBuilder.html

Cheers,
Thorben

Another thought: What about having something similar to ReflectionToStringBuilder for Camunda API objects?

Could be a class CamundaObjectFormatter that has a static toString(Object) method. It will then detect all implemented public API interfaces and build a String based on the interfaces’ getter methods (probably only those that return String or a primitive value). That could go into a standalone library/community extension so that it works with any Camunda version and in addition we could shade it into the core codebase and use it for toString implementations there. That way, the effort for maintaining toString implementations is zero (e.g. when adding new public API methods), and you can still easily help yourself when we forgot implementing toString.

I’d prefer such a solution that solves the problem once and for good over a solution that requires ongoing development effort, even if it is a small one.

Cheers,
Thorben

Hi,

in the web modeler we use project lombok which is also able to create toString() methods.

Cheers,
Sebastian

Here’s a simple implementation of the idea I posted above:

package org.camunda.bpm.unittest;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class CamundaObjectFormatter {


  public static String toString(Object o) {
    Class<?>[] implementedInterfaces = o.getClass().getInterfaces();

    StringBuilder sb = new StringBuilder();

    sb.append("[");

    for (Class<?> implementedInterface : implementedInterfaces) {
      if (isPublicApi(implementedInterface)) {
        sb.append(implementedInterface.getSimpleName());
        sb.append("(");

        Method[] interfaceMethods = implementedInterface.getMethods();

        for (Method interfaceMethod : interfaceMethods)
        {
          if (isUsefulForStringification(interfaceMethod))
          {
            sb.append(interfaceMethod.getName());
            sb.append(": ");
            try {
              sb.append(interfaceMethod.invoke(o));

            } catch (InvocationTargetException e) {
              // ignore; some getters throw an exception depending on API usage
            } catch (Exception e) {
              throw new RuntimeException(e);
            }
            sb.append(", ");
          }
        }

        sb.append("), ");
      }
    }
    sb.append("]");

    return sb.toString();
  }

  private static boolean isPublicApi(Class<?> implementedInterface) {
    String interfaceName = implementedInterface.getName();

    return interfaceName.startsWith("org.camunda.") && !interfaceName.contains(".impl.");
  }

  private static boolean isUsefulForStringification(Method method)
  {
    Class<?> returnType = method.getReturnType();

    return method.getParameterTypes().length == 0
        && (returnType.isPrimitive()
          || returnType == String.class
          || returnType == Long.class
          || returnType == Integer.class
          || returnType == Double.class
          || returnType == Short.class);
  }

}
1 Like