code: @SuppressWarnings("unchecked")
      protected Class<T> determineBasicMessageClass() {
          // all of this is usually going to just return AbstractMessage.class - but in case there is a subclass hierarchy
          // that makes it more specific, this will help discover the message class.
          Class<?> thisClazz = this.getClass();
          Type superClazz = thisClazz.getGenericSuperclass();
  
          // we might be a internal generated class (like a MDB within a container) so walk up the hierarchy
          // to find our real paramaterized superclass
          while (superClazz instanceof Class) {
              superClazz = ((Class<?>) superClazz).getGenericSuperclass();
          }
          ParameterizedType parameterizedType = (ParameterizedType) superClazz;
          Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
          Class<T> clazz;
          if (actualTypeArgument instanceof Class<?>) {
              clazz = (Class<T>) actualTypeArgument;
          } else {
              TypeVariable<?> typeVar = (TypeVariable<?>) actualTypeArgument;
              clazz = (Class<T>) typeVar.getBounds()[0];
          }
          return clazz;
      }
docs: In order to decode the JSON, we need the class representation of the basic message type. This method uses
  reflection to try to get that type.
  
  Subclasses can override this if they want to provide the class representation themselves (e.g. in case the
  reflection cannot get it). Alternatively, subclasses can utilize the constructor
  {@link AbstractBasicMessageListener#AbstractBasicMessageListener(Class)} to tell this object what the class of T
  is.
  
  @return class of T