3

I’m resurrecting an old application that worked with WildFly 8 / HornetMQ / Log4J. It is a remote Java GUI Client that swaped JMS ObjectMessages.

So far everything works again when reconfigured for WildFly 10 / ActiveMQ / Slf4j, except one thing:

After my MDB receives an ObjectMessage (whose Object is an ArrayList) it throws a JMSException error when the MDB calls getObject() on the message. JMSExcetion. e.getMessage() returns:

org.slf4j.log4j12.Log4jLoggerAdapter from 
[Module "org.apache.activemq.artemis:main" from local module loader @1c2c22f3 
    (finder: local module finder @18e8568 
        (roots: C:\ProgramFilesGeo\Wildfly\wildfly-10.1.0.Final\modules,
           C:\ProgramFilesGeo\Wildfly\wildfly-10.1.0.Final\modules\system\layers\base)

But if I remove Slf4J logging from the EntityParent instances in the ArrayList all works correctly … except I then can’t log from the EntityParent subclass the MDB extracts from the ObjectMessage.

Here’s what I know so far:

It's clearly is an issue with Slf4j and I guess Serialization. Because otherwise, Slf4j works fine on the server side. I mean the message above was written to the WF log with this logger code in the MDB:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final Logger logger = Logger.getLogger(GoMsgBean.class.getName());

My MDB confirms the message arrives as: ActiveMQObjectMessage

Before transmitting the ObjectMessage I tested the instance of EntityParent and the ArrayList for Serialization – ability with:

new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(each of the above objects separately);

A web search brings up one match to my issue – getObject() throwing a JMSException. But its marked as Not Answered. And what was posted was so wrapped in a discussion of a proxy servers and class loaders as to be unhelpful.

I found other discussions related to this problem with a Log4j logger. They suggested annotating the logger as @Transient. But then claimed switching to slf4j “solved” that problem without making it @Transient. So I both switched to slf4j and marked it @Transient. No all loggers in all subclasses of EntityParent in the ArrayList used:

@Transient
final Logger logger = LoggerFactory.getLogger(LoggedIn.class);

The slf4j website discusses other problems, but they all seem to involve cases where the application tries to retain log4j logging. Not my case anymore.

So I hope someone can help, because I’m not sure where else to look … my real job is to deal with the data base once the MDB can extract the entities.

In case it’s of any use, here’s the activeMQ subsystem of standalone-full.xml

<subsystem xmlns="urn:jboss:domain:messaging-activemq:1.0">
    <server name="default">
        <security enabled="false"/>
        <security-setting name="#">
            <role name="guest" send="true" consume="true" create-non-durable-queue="true" delete-non-durable-queue="true"/>
        </security-setting>
        <address-setting name="#" dead-letter-address="jms.queue.DLQ" expiry-address="jms.queue.ExpiryQueue" max-size-bytes="10485760" page-size-bytes="2097152" message-counter-history-day-limit="10"/>
        <http-connector name="http-connector" socket-binding="http" endpoint="http-acceptor"/>
        <http-connector name="http-connector-throughput" socket-binding="http" endpoint="http-acceptor-throughput">
    <param name="batch-delay" value="50"/>
        </http-connector>
        <in-vm-connector name="in-vm" server-id="0"/>
        <http-acceptor name="http-acceptor" http-listener="default"/>
        <http-acceptor name="http-acceptor-throughput" http-listener="default">
            <param name="batch-delay" value="50"/>
            <param name="direct-deliver" value="false"/>
        </http-acceptor>
        <in-vm-acceptor name="in-vm" server-id="0"/>
        <jms-queue name="ExpiryQueue" entries="java:/jms/queue/ExpiryQueue"/>
        <jms-queue name="DLQ" entries="java:/jms/queue/DLQ"/>
        <jms-queue name="SendToServerQueue" entries="java:jboss/exported/jms/queue/sendToServerQueue java:/jms/queue/sendToServerQueue"/>
        <jms-queue name="SendToClientQueue2" entries="java:jboss/exported/jms/queue/sendToClientQueue2"/>
        <connection-factory name="InVmConnectionFactory" entries="java:/ConnectionFactory" connectors="in-vm"/>
        <connection-factory name="RemoteConnectionFactory" entries="java:jboss/exported/jms/RemoteConnectionFactory" connectors="http-connector"/>
        <pooled-connection-factory name="activemq-ra" entries="java:/JmsXA java:jboss/DefaultJMSConnectionFactory" connectors="in-vm" transaction="xa"/>
    </server>
</subsystem>

This is added for the bounty: It is a copy of my current JPA Entity (POJO) class. It works perfectly w/ all logging commented out. But it fails with the error above when the slf4j logger is added back in. Even though slf4j loggers work fine for the MDB, and Session Beans, I deploy with the ear package.

package org.america3.gotest.shared.jpa;

//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.america3.gotest.shared.interfaces.DataItemKeyValues;
import org.america3.gotest.shared.tools.Utility;

@NamedQueries(
{@NamedQuery(name="LoggedIn.findLoggedInbyId",
             query = "SELECT li "  +
                     "From LoggedIn li "   + 
                     "WHERE li.memberId = :memberIdP"),
 @NamedQuery(name="findLoggedInByAll",
             query = "DELETE FROM LoggedIn li " +
                     "WHERE li.memberId = :memberIdP " +
                     "AND   li.memberPw = :memberPwP " +
                     "AND   li.clientHash = clientHashP")
})

@Entity(name="LoggedIn")
@Table(name="loggedins")
public class LoggedIn extends EntityParent implements Serializable, DataItemKeyValues {
  /* See EntityParent for explanation of 9 fields
   * This subclass only persists memberId, memberPW, and clientHash
   */

  //@Transient
  //final Logger logger = LoggerFactory.getLogger(LoggedIn.class);

  //constructors
  public LoggedIn() {
    this.setMemberId(NOT_APPLICABLE);
    this.setMemberPw(NOT_APPLICABLE);
    this.SetClientHash(NOT_APPLICABLE);
    this.setClientUserId(NOT_APPLICABLE);
    this.setClientUserPw(NOT_APPLICABLE);
    this.setOwnerId(NOT_APPLICABLE);
    this.setOwnerPw(NOT_APPLICABLE);
    this.setOpponentId(NOT_APPLICABLE);
    this.setOpponentPw(NOT_APPLICABLE);
  };

  public LoggedIn(String hash, String id, String pw) {   
    this();
    if (id != null && pw !=null && hash != null) {
      this.setMemberId(id);
      this.setMemberPw(pw);
      this.SetClientHash(hash);
    } else {
      //logger.error("Log in was attempted with: id \"" + id + "\" pw \"" + hash + "\"");
      String iAmM = Utility.getIAmM(Thread.currentThread().getStackTrace());
      System.err.println(iAmM + "Log in was attempted with: id \"" + id + "\" pw \"" + hash + "\"");
    }
  }

  public LoggedIn(EntityParent loggedIn) {
    this();
    this.memberId = loggedIn.getMemberId();
    this.memberPw = loggedIn.getMemberPw();
    this.clientHash = loggedIn.getClientHash();
  }

  // persisted fields
  @Id @Column(name="PK")
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  private Integer pk;

  @Column(name="MBR_ID")
  private String memberId;

  @Column(name="MBR_PW")
  private String memberPw;

  @Column(name="HASH")
  private String clientHash;

  // Transient fields
  @Transient
  private String clientUserId;
  @Transient
  private String clientUserPw;
  @Transient
  private String ownerId;
  @Transient
  private String ownerPw;
  @Transient
  private String opponentId;
  @Transient
  private String opponentPw;

  private static final long serialVersionUID = 1L;  

  // get set methods
  public Integer getPk () {return this.pk;}
  public void setPk (Integer pk) {this.pk = pk;}

  public String getMemberId () {return memberId;}
  public void   setMemberId (String memberId) {this.memberId = (memberId == null? "" : memberId);}

  public String getMemberPw () {return this.memberPw;}
  public void   setMemberPw (String memberPw) {this.memberPw = (memberPw == null? "" : memberPw);}

  public String getClientHash () {return clientHash;}
  public void   SetClientHash (String clientHash) {this.clientHash = (clientHash == null? "" : clientHash);}

  public boolean equals (LoggedIn otherLoggedIn) {
    if (otherLoggedIn == null) return false;
    if (this.pk == otherLoggedIn.getPk()) {
      return true;
    } else {
      return false;
    }
  }

  // other methods
  @Transient
  public String getClientUserId () {return this.clientUserId;}
  public void   setClientUserId (String clientUserId) {this.clientUserId = (clientUserId == null? "" : clientUserId);}

  @Transient
  public String getClientUserPw () {return this.clientUserPw;}
  public void   setClientUserPw (String clientUserPw) {this.clientUserPw = (clientUserPw == null? "" : clientUserPw);}

  @Transient
  public String getOwnerId () {return this.ownerId;}
  public void   setOwnerId (String ownerId) {this.ownerId = (ownerId == null? "" : ownerId);}

  @Transient
  public String getOwnerPw () {return ownerPw;}
  public void   setOwnerPw (String ownerPw) {this.ownerPw = (ownerPw == null? "" : ownerPw);}

  @Transient
  public String getOpponentId () {return opponentId;}
  public void   setOpponentId (String opponentId) {this.opponentId = (opponentId == null? "" : opponentId);}

  @Transient
  public String getOpponentPw () {return opponentPw;}
  public void   setOpponentPw (String opponentPw) {this.opponentPw = (opponentPw == null? "" : opponentPw);}


  public void writeEntity () {
    //String iAmS = Utility.getIAmS(Thread.currentThread().getStackTrace());
    //logger.info(iAmS + this.getClass().getSimpleName() + "  contains these values");
    //logger.info(iAmS + "  PK (pk)             : " + this.pk);
    //logger.info(iAmS + "  MBR_ID (memberId)   : " + this.memberId);
    //logger.info(iAmS + "  MBR_PW (memberPw)   : " + this.memberPw);
    //logger.info(iAmS + "  HASH (clientHash)   : " + this.clientHash);
    //logger.info(iAmS + "  Trans (clientUserId): " + this.clientUserId);
    //logger.info(iAmS + "  Trans (clientUserPw): " + this.clientUserPw);
  }

}
George
  • 509
  • 2
  • 9
  • 25
  • Do you exclude the logging subsystem or logging dependencies in your deployment? – James R. Perkins Sep 12 '17 at 22:55
  • Opps ... comment above got away before I could mention I found a module.xml artifact built into the .ear's META-INFO folder when depoloyed. That's it above. Don't know how to format CODE in these comments. But I removed it and ... no change. Same error on getObject(). Otherwise I didn't intentionally add an dependencies in my deployment. Other xml files built into the ear are application.xml and persistance.xml and neither address logging. – George Sep 14 '17 at 00:33
  • And I found this in [wildfly-10.1.0.Final\modules\system\layers\base\org\jboss\logging\main\module.xml] … (I'm off to find out how to make Code look like code) – George Sep 14 '17 at 00:41
  • Sorry ... this is just a test I could only try a real comment: 'line 1 line2 line 3' ? Post said back tics would format code. But doesn't seem to – George Sep 14 '17 at 00:53
  • Or maybe ` Line 1 Line 2 Line3` will work. Nope ... but I know I've seen short code in comments ... anyone else know how? – George Sep 14 '17 at 01:01
  • Do you have a `jboss-deployment-structure.xml` in your deployment? – James R. Perkins Sep 14 '17 at 17:01
  • James. No. But the deployment is flawless, i.e. everything works correctly when I remove stf4j logger from the object the client sends to the MDB. Including all stf4j logging done by the MDB and all the Session Beans. It's only when I leave the stf4j logging in the object carried by the ObjectMessage that getObject() throws an exception. Its really has to be a serialization issue with stf4j logger. Wildfly has a horribly complicated xmd descriptor for a jboss-deployement-structure.xml, but I can't see anything in it that obviously deals with stf4j logging. Afraid to mess with it w/o specifics. – George Sep 22 '17 at 02:08
  • Using slf4j should work fine. Ideally you wouldn't want to include a slf4j-api in your deployment or an implementation. WildFly will automatically add module dependencies to your deployment. – James R. Perkins Sep 22 '17 at 16:03
  • Hi James,That's exactly my problem. slf4j work fine with unmodified WF-10 (standalone-full.xml). My MDB uses it and logs fine. My Session Beans use it and log fine.Both Client and Server side JMS works fine (i.e. Object Message is sent and received fine). Remove the slf4j logger from my sent object and the JMS Object Message getObject() works fine and my MDB/Session Beans process it fine). It's only when the object I send has a slf4j Logger that the JMS ObjectMessage getObject() thows an Exception. So it HAS TO BE de-Serialization ... doesn't it? And I can't find any solution anywhere. – George Sep 23 '17 at 22:40
  • Hey Stack Overflow. No one came close to offering even a hint of an answer to my problem. Now you say you are going to charge me anyway. Seems wrong, but ... your house your rules. So can you give them to a charity? But between you and me, taking stuff for no help ... well ... not a thing I'd do. What's up? – George Sep 25 '17 at 01:36

1 Answers1

2

Make the logger variable transient, by which I do not mean @Transient.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Thank you. It really was that simple. So I owe you the bounty. But for the life of me I have no idea how to award it. Or maybe Stack Overflow stole it when it expired. Do you know if I can still award it. or do I have to post another one to pay up? This system really needs a bit better explanation. – George Sep 27 '17 at 01:00
  • I agree, it does. Once it's gone it's gone, I believe. You could ask on meta about it. – user207421 Sep 27 '17 at 03:00