Hibernate envers. Changing the user ID of the person who made the change

Good afternoon, dear Khabrovites. This is my first article, please do not swear much.
Hibernate has already written a lot about listening. I want to talk about solving a not quite standard task - writing to the revision table the ID of any user assigned immediately before the operation of writing the entity to the database. The standard solution proposed in the official documentation is to use the user ID stored in the session component. But there may be a situation where the user ID must be changed. Example: a user performs operations through interaction with a telephony server through DTMF signals. In this case, you do not need to create a session at all. I have been searching for a solution on the Internet for a long time, but I haven’t found anything, so I offer you my version. Perhaps for some of the newcomers, like me, it will be useful.
After reading the documentation, I realized that listening in Hibernate is based on interceptors. This means that the thread updating the entity in the database is responsible for listening - this is already something.
Let's try to make use of it. We create a stateless component in which a static map with pairs will be stored: stream ID - user ID. The start method adds the user ID (passed by the parameter) and the ID of the current thread to the map, then starts the method in the new transaction that performs the necessary actions, waits for the method (transaction) to finish, and removes the stream ID and user from the map.
@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class FakeOwnerTransaction {
@Inject
private Provider providerFakeOwnerNewTransaction;
private static ThreadLocal threadOwnerID = new ThreadLocal<>()
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void newCMT(Runnable action) {
action.run();
}
public void start(Long personID, Runnable action) {
threadOwnerID.set(personID);
try {
providerFakeOwnerNewTransaction.get().newCMT(action);
} finally {
threadOwnerID.remove();
}
}
public static Long getFakeChanger() {
return threadOwnerID.get();
}
}
Now let's see what the RevisionListener implementation will look like. If the current thread is associated with a user ID, use this ID, otherwise we take the user ID from the session component of UserManager.
public class Audition implements RevisionListener {
@Override
public void newRevision(Revinfo revinfo) {
Long personID = FakeOwnerTransaction.getFakeChanger();
if (personID == null) {
UserManager userManager = SystemUtils.lookup(JNDI_NAME_PREFIX, UserManager.class);
personID = userManager.getPersonID();
}
revinfo.setPersonID(personID);
}
}
And finally, let's try to make a change in the database using the ID of the specified user.
FakeOwnerTransaction fakeOwnerTransaction = SystemUtils.lookup(JNDI_NAME_PREFIX, FakeOwnerTransaction.class);
fakeOwnerTransaction.start(getPersonID(), new Runnable() {
@Override
public void run() {
Dao dao = SystemUtils.lookup(JNDI_NAME_PREFIX, Dao.class);
dao.add(new Person(“Smirmov”));
}
});
public class SystemUtils {
public static T lookup(String jndiNamePrefix, Class clazz) {
String jndiName = jndiNamePrefix;
jndiName += clazz.getSimpleName() + "!" + clazz.getName();
try {
return (T) new InitialContext().lookup(jndiName);
} catch (Exception e) {
CoreSharedUtils.getLogger().severe("Error. Bean '" + jndiName + "' not found!");
e.printStackTrace();
}
return null;
}
…
}
A thread can be terminated from outside the JVM, in which case the finaly block will not be executed. Hypothetically, this can lead to a memory leak, but this option suits me.
That’s all for me. If anyone has more interesting solutions or criticism, welcome to the comments.