<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anuj Singh</title>
    <description>The latest articles on DEV Community by Anuj Singh (@anuj-singh1).</description>
    <link>https://dev.to/anuj-singh1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1666090%2F6986c59d-ac94-40d2-bec6-9443f7e928fc.gif</url>
      <title>DEV Community: Anuj Singh</title>
      <link>https://dev.to/anuj-singh1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anuj-singh1"/>
    <language>en</language>
    <item>
      <title>Enhancing Transparency and Accountability: Implementing Entity Audit Logging in Java [Part-2/2]</title>
      <dc:creator>Anuj Singh</dc:creator>
      <pubDate>Fri, 28 Jun 2024 11:01:41 +0000</pubDate>
      <link>https://dev.to/anuj-singh1/enhancing-transparency-and-accountability-implementing-entity-audit-logging-in-java-part-22-3gm7</link>
      <guid>https://dev.to/anuj-singh1/enhancing-transparency-and-accountability-implementing-entity-audit-logging-in-java-part-22-3gm7</guid>
      <description>&lt;p&gt;In the last post, we used io.ebean's &lt;code&gt;ChangeLog&lt;/code&gt; annotation to log entity changes in a synchronous blocking I/O application. In this post, we will see how that works in a multithreaded or async (non-blocking) I/O application. &lt;/p&gt;

&lt;p&gt;If your system uses a separate thread-pool or executor context for doing I/O tasks such as making an HTTP API request or a database transaction, then, the audit log prepare class will not be able to detect the user context on model entities that are updated, as the model entity will be saved in a different context or thread-pool. Hence, we need to ensure that the thread-context is not lost when switching the executor or when switching threads in the same thread-pool. &lt;/p&gt;

&lt;p&gt;To do so, first we need to understand how to store some data in thread context. For this we will use ThreadLocals. ThreadLocal construct allows us to store data that will be accessible only by a specific thread. Each thread will have its own ThreadLocal instance, hence while switching the context, we need to ensure that the thread context of the current thread is propagated to the next thread.&lt;/p&gt;

&lt;h2&gt;
  
  
  STORING USER CONTEXT IN THREAD LOCALS
&lt;/h2&gt;

&lt;p&gt;We can set ThreadLocals with user info and use it in change log prepare’s implementation to set it in change set.&lt;/p&gt;

&lt;h3&gt;
  
  
  ThreadLocalManager
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ThreadLocalManager {
    private static ThreadLocal&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; context = new ThreadLocal&amp;lt;&amp;gt;();
    public static void addToContext(String key, Object value) {
        Map&amp;lt;String, Object&amp;gt; currentContext = ThreadLocalManager.context.get() == null ? new HashMap&amp;lt;&amp;gt;() : context.get();
        currentContext.put(key, value);
        context.set(currentContext);
    }
    public static void setContext(Map&amp;lt;String, Object&amp;gt; contextMap) {
        context.set(contextMap);
    }
    public static Map&amp;lt;String, Object&amp;gt; getContext() {
        return context.get();
    }
    public static Object getFromContext(String key) {
        return Nullifier.get(() -&amp;gt; context.get().getOrDefault(key, "NA"));
    }
    public static void clearContext() {
        ThreadLocalManager.context.remove();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can add additional info before setting the thread local&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ThreadLocalManager.addToContext("userContext", new HashMap&amp;lt;String, String&amp;gt;() {{
    put("userName", "some user");
    put("userEmail", "some.user@company.com");
}}
entity.save()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we'll modify our ChangeLogPrepare to read user data from thread context&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom ChangeLogPrepare
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class AuditLogPrepare implements ChangeLogPrepare {
    private final play.Logger.ALogger logger = Logger.of(this.getClass());
    @Override
    public boolean prepare(ChangeSet changes) {
        Map&amp;lt;String, String&amp;gt; userContext = Nullifier.get(() -&amp;gt; (Map&amp;lt;String, String&amp;gt;) ThreadLocalManager.getContext().get("userContext"), new HashMap&amp;lt;&amp;gt;());
        if (userContext.isEmpty()) logger.warn("[ALERT] userContext is empty for changeset: " + changes.toString());
        changes.getUserContext().put("userName", authMap.getOrDefault("userName", DEFAULT_USER_NAME));
        changes.getUserContext().put("userEmail", authMap.getOrDefault("userEmail", DEFAULT_USER_EMAIL));
        changes.setSource("MyApp");
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we see, the user data is taken from thread local, we need to ensure that the thread context is maintained while switching the thread. For that, we'll create a utility class that helps us propagate the thread context to next runnable/callable.&lt;/p&gt;

&lt;h3&gt;
  
  
  ContextUtility
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ContextUtility {
    public static &amp;lt;T&amp;gt; Callable&amp;lt;T&amp;gt; wrapWithContext(Callable&amp;lt;T&amp;gt; task) {
        Map&amp;lt;String, Object&amp;gt; previousContext = ThreadLocalManager.getContext();
        if (previousContext == null)
            return task;
        else
            return () -&amp;gt; {
                ThreadLocalManager.setContext(previousContext);
                try {
                    return task.call();
                } finally {
                    ThreadLocalManager.clearContext();
                }
            };
    }
    public static Runnable wrapWithContext(Runnable task) {
        Map&amp;lt;String, Object&amp;gt; previousContext = ThreadLocalManager.getContext();
        if (previousContext == null) {
            return task;
        } else
            return () -&amp;gt; {
                ThreadLocalManager.setContext(previousContext);
                try {
                    task.run();
                } finally {
                    ThreadLocalManager.clearContext();
                }
            };
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the methods from ContextUtility we create CustomThreadPoolExecutor to override methods to attach thread context before submitting/executing tasks&lt;/p&gt;

&lt;h3&gt;
  
  
  CustomThreadPoolExecutor
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
    public CustomThreadPoolExecutor(int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            @NotNull TimeUnit unit,
            @NotNull BlockingQueue&amp;lt;Runnable&amp;gt; workQueue,
            @NotNull ThreadFactory threadFactory,
            @NotNull RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }
    @Override
    public &amp;lt;T&amp;gt; @NotNull Future&amp;lt;T&amp;gt; submit(@NotNull Callable&amp;lt;T&amp;gt; task) {
        return super.submit(ContextUtility.wrapWithContext(task));
    }
    @Override
    public &amp;lt;T&amp;gt; @NotNull Future&amp;lt;T&amp;gt; submit(@NotNull Runnable task, T result) {
        return super.submit(ContextUtility.wrapWithContext(task), result);
    }
    @Override
    public @NotNull Future&amp;lt;?&amp;gt; submit(@NotNull Runnable task) {
        return super.submit(ContextUtility.wrapWithContext(task));
    }
    @Override
    public void execute(@NotNull Runnable task) {
        super.execute(ContextUtility.wrapWithContext(task));
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we will use this executor in our custom MDC to allow creating custom dispatchers.&lt;/p&gt;

&lt;h3&gt;
  
  
  CustomDispatcherConfigurator
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class CustomDispatcherConfigurator extends MessageDispatcherConfigurator {
    private final CustomDispatcher instance;
    public CustomDispatcherConfigurator(Config config, DispatcherPrerequisites prerequisites) {
        super(config, prerequisites);
        Config threadPoolConfig = config.getConfig("thread-pool-executor");
        int fixedPoolSize = threadPoolConfig.getInt("fixed-pool-size");
        instance = new CustomDispatcher(
                this,
                config.getString("id"),
                config.getInt("throughput"),
                Duration.create(config.getDuration("throughput-deadline-time", TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS),
                (id, threadFactory) -&amp;gt; () -&amp;gt; new CustomThreadPoolExecutor(fixedPoolSize,
                                                                          fixedPoolSize,
                                                                          threadPoolConfig.getDuration("keep-alive-time", TimeUnit.MILLISECONDS),
                                                                          TimeUnit.MILLISECONDS,
                                                                          new LinkedBlockingDeque&amp;lt;&amp;gt;(),
                                                                          new ThreadFactory() {
                                                                              private int threadId = 1;
                                                                              @Override
                                                                              public Thread newThread(@NotNull Runnable r) {
                                                                                  Thread thread = new Thread(r);
                                                                                  thread.setName(config.getString("name") + "-" + threadId++);
                                                                                  return thread;
                                                                              }
                                                                          }),
                Duration.create(config.getDuration("shutdown-timeout", TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS)
        );
    }
    @Override
    public MessageDispatcher dispatcher() {
        return instance;
    }
}
class CustomDispatcher extends Dispatcher {
    public CustomDispatcher(MessageDispatcherConfigurator _configurator,
                            String id,
                            int throughput,
                            Duration throughputDeadlineTime,
                            ExecutorServiceFactoryProvider executorServiceFactoryProvider,
                            scala.concurrent.duration.FiniteDuration shutdownTimeout) {
        super(_configurator, id, throughput, throughputDeadlineTime, executorServiceFactoryProvider, shutdownTimeout);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can create different actors in our actor system using this custom MDC and define their config in aplication.conf&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db-io-dispatcher {
    type = "contexts.CustomDispatcherConfigurator"
    executor = "thread-pool-executor"
    thread-pool-executor {
        fixed-pool-size = 11
    }
    throughput = 1
    shutdown-timeout = 60s
}
web-io-dispatcher {
    type = "contexts.CustomDispatcherConfigurator"
    executor = "thread-pool-executor"
    thread-pool-executor {
        fixed-pool-size = 20
    }
    throughput = 1
    shutdown-timeout = 60s
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To create an actor for db execution context&lt;/p&gt;

&lt;h3&gt;
  
  
  DatabaseIODispatcher
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class DatabaseIODispatcher extends CustomExecutionContext {
    @Inject
    public DatabaseIODispatcher(ActorSystem actorSystem) {
        super(actorSystem, "db-io-dispatcher");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to switch context from one executor to another without losing thread’s context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Points to Remember
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In async implementations, if the thread is switched in-between from custom executors to default ThreadPoolExecutor or ForkJoinPool, the thread context will get lost, hence we need to ensure that the thread context is not getting lost if any library method is using default pools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We need to clear thread context after the task is complete or else it can cause memory leaks or OOM issues.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading. I hope this helps the community to provide more transparency in their applications.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>java</category>
    </item>
    <item>
      <title>Enhancing Transparency and Accountability: Implementing Entity Audit Logging in Java [Part-1]</title>
      <dc:creator>Anuj Singh</dc:creator>
      <pubDate>Sat, 22 Jun 2024 09:58:10 +0000</pubDate>
      <link>https://dev.to/anuj-singh1/enhancing-transparency-and-accountability-implementing-entity-audit-logging-in-java-1f3f</link>
      <guid>https://dev.to/anuj-singh1/enhancing-transparency-and-accountability-implementing-entity-audit-logging-in-java-1f3f</guid>
      <description>&lt;p&gt;In the dynamic world of Java development, I embarked on a mission to fulfil a client’s request for comprehensive user activity logs across our company’s services. To achieve this, I turned to Ebean’s changelog feature, a robust tool for audit logging in Java applications.&lt;/p&gt;

&lt;p&gt;Initially, I experimented with manual logging by modifying the save method of our model entities. However, this approach proved cumbersome and prone to errors, lacking the comprehensive tracking capabilities we required. Additionally, I explored Ebean’s History annotation, hoping it would streamline the audit logging process. Unfortunately, it didn’t meet our needs as it lacked the granularity and customisation options necessary for our application.&lt;/p&gt;

&lt;p&gt;Undeterred, I focused on leveraging Ebean’s changelog functionality to seamlessly integrate audit logging into our model entities. Each modification became a part of a detailed activity log, providing insight into users’ actions across our services.&lt;/p&gt;

&lt;p&gt;As the development progressed, the application transformed into a testament to our commitment to data integrity. Stakeholders applauded the newfound visibility into user activity, while compliance requirements were effortlessly met. Through the power of Ebean’s Changelog and my dedication to meeting client needs, our Java application emerged as a beacon of transparency, paving the way for a more accountable future in our services.&lt;/p&gt;

&lt;h2&gt;
  
  
  BASIC ENTITY AUDIT LOGS
&lt;/h2&gt;

&lt;p&gt;To enable audit logging for an entity we need to first enable &lt;code&gt;@ChangeLog&lt;/code&gt; annotation from &lt;code&gt;io.ebean.annotation&lt;/code&gt; on the entity. By default inserts are included. It can be excluded if required using &lt;code&gt;inserts = ChangeLogInsertMode.EXCLUDE&lt;/code&gt; option in the annotation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
@ChangeLog
@Table(name = "datagroup")
public class DataGroup extends Model {
  ...
@ChangeLog(inserts = ChangeLogInsertMode.EXCLUDE)
@Entity
public class Dataset extends Model {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to log entity change only on certain field changes, then we can use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ChangeLog(updatesThatInclude = {"field1","field2"})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need to define a log appender for ChangeLogs in logback.xml. In our example we are using a ConsoleAppender to log out to STDOUT, however there are many options to log to socket, Files, DB, Kafka, SMTP or even slack and other apps. Refer &lt;a href="https://logging.apache.org/log4j/2.x/manual/appenders.html"&gt;this link&lt;/a&gt; for various appenders from apache and its not limited to this. There are other libraries offering more options as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&amp;gt;
    &amp;lt;encoder&amp;gt;
        &amp;lt;pattern&amp;gt;%date{yyyy-MM-dd HH:mm:ss} %coloredLevel %logger{15} - %message%n%xException{10}&amp;lt;/pattern&amp;gt;
    &amp;lt;/encoder&amp;gt;
&amp;lt;/appender&amp;gt;
&amp;lt;logger name="io.ebean.ChangeLog" level="TRACE" additivity="false"&amp;gt;
    &amp;lt;appender-ref ref="STDOUT"/&amp;gt;
&amp;lt;/logger&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is sufficient to log entity changes (inserts and updates).&lt;/p&gt;

&lt;h2&gt;
  
  
  AUDIT LOGS WITH USER CONTEXT
&lt;/h2&gt;

&lt;p&gt;To log user context or additional info along with the changes to the entity, we need to implement ChangeLogPrepare class to add the additional info to the change set. The implementation of ChangeLogPrepare can be automatically detected if classpath scanning is on (just like entity beans are found). That is, if scanning is on we don’t need to explicitly register the ChangeLogPrepare implementation and instead it will be found and instantiated. We can also register it explicitly in DatabaseConfig as used in &lt;a href="https://ebean.io/docs/features/changelog"&gt;this doc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For our use-case, we’ll keep the implementations inside models folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyChangeLogPrepare implements ChangeLogPrepare {
  @Override
  public boolean prepare(ChangeSet changes) {
// get user context information typically from a ThreadLocal or similar mechanism
    String currentUserId = ...;
    changes.setUserId(currentUserId);
    String userIpAddress = ...;
    changes.setUserIpAddress(userIpAddress);
    changes.setSource("myApplicationName");
// add arbitrary user context information to the userContext map
    changes.getUserContext().put("some", "thing");
    return true;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the next post, we'll take a look into how all this fits in an asynchronous application where execution contexts can change frequently and the change context might get lost in thread switching. &lt;/p&gt;

</description>
      <category>programming</category>
      <category>java</category>
    </item>
  </channel>
</rss>
