This is an automated email from the ASF dual-hosted git repository.

stefanegli pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-event.git


The following commit(s) were added to refs/heads/master by this push:
     new dae8b46  SLING-11793 : limit log messages count for 
JobExecutionContext (#24)
dae8b46 is described below

commit dae8b46d2ea1d4cbfaeaf75e665b62d86a924abb
Author: Rishabh Kumar <[email protected]>
AuthorDate: Wed Mar 15 16:39:21 2023 +0530

    SLING-11793 : limit log messages count for JobExecutionContext (#24)
    
    * SLING-11793 : limit log messages count for JobExecutionContext
    
    * SLING-11793 : fixed OOM if we initialized ArrayDeque with 
Integer.MAX_VALUE
    
    * SLING-11793 : fixed cases where max progressLog count could be 0 or lower 
than default size of ArrayDeque
    
    * SLING-11793 : ensured backward compatability while setting/getting 
progressLog property on Job
    
    * SLING-11793 : fixed issue when old job doesn't have progressLog max count 
property
    
    * SLING-11793 : removed slingevent:progresslog:maxCount as JCR property and 
used as an instance variable
    
    * SLING-11793 : fixed issue when migrating from old job to new job with max 
count limit
    
    * SLING-11793 : fixed compatibility issue by keeping the PROGRESS_LOG 
property as String[]
    
    * SLING-11793 : removed wiring of progressLogMaxCount and passed this 
variable as method parameter to JobImpl
    
    * SLING-11793 : removed wiring of progressLogMaxCount
    
    * SLING-11793 : appended truncated log message in case we had to trim 
progressLog message due to maxLog Count
    
    * SLING-11793 : improved documentation of progresslog_maxCount config to 
add that using 0 would discard all the logs
    
    * SLING-11793 : returned the tests to their default behaviour of having 
unlimited log messages
    
    * SLING-11793 : skipped adding truncated log message if already there in log
    
    ---------
    
    Co-authored-by: Rishabh Kumar <[email protected]>
---
 .../apache/sling/event/impl/jobs/JobHandler.java   |  12 ++-
 .../org/apache/sling/event/impl/jobs/JobImpl.java  |  34 +++++--
 .../impl/jobs/config/JobManagerConfiguration.java  |  22 +++++
 .../impl/jobs/queues/JobExecutionContextImpl.java  |   3 +-
 .../apache/sling/event/impl/jobs/JobsImplTest.java | 100 ++++++++++++++++++++-
 .../config/JobManagerConfigurationTestFactory.java |   5 +-
 6 files changed, 167 insertions(+), 9 deletions(-)

diff --git a/src/main/java/org/apache/sling/event/impl/jobs/JobHandler.java 
b/src/main/java/org/apache/sling/event/impl/jobs/JobHandler.java
index 1a0ae86..12e2221 100644
--- a/src/main/java/org/apache/sling/event/impl/jobs/JobHandler.java
+++ b/src/main/java/org/apache/sling/event/impl/jobs/JobHandler.java
@@ -51,6 +51,11 @@ public class JobHandler {
 
     private final JobManagerConfiguration configuration;
 
+    /**
+     * Max number of log message that can stored by consumer to add 
information about current state of Job.
+     */
+    private final int progressLogMaxCount;
+
     private final JobExecutor consumer;
 
     public JobHandler(final JobImpl job,
@@ -59,12 +64,17 @@ public class JobHandler {
         this.job = job;
         this.consumer = consumer;
         this.configuration = configuration;
+        this.progressLogMaxCount = configuration.getProgressLogMaxCount();
     }
 
     public JobImpl getJob() {
         return this.job;
     }
 
+    public int getProgressLogMaxCount() {
+        return progressLogMaxCount;
+    }
+
     public JobExecutor getConsumer() {
         return this.consumer;
     }
@@ -281,5 +291,5 @@ public class JobHandler {
         }
         
     }
-    
+
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/event/impl/jobs/JobImpl.java 
b/src/main/java/org/apache/sling/event/impl/jobs/JobImpl.java
index ab4ba1b..f5956f7 100644
--- a/src/main/java/org/apache/sling/event/impl/jobs/JobImpl.java
+++ b/src/main/java/org/apache/sling/event/impl/jobs/JobImpl.java
@@ -62,6 +62,8 @@ public class JobImpl implements Job, Comparable<JobImpl> {
      */
     public static final String PROPERTY_FINISHED_STATE = 
"slingevent:finishedState";
 
+    static final String TRUNCATED_LOG = "...truncated log";
+
     private final ValueMap properties;
 
     private final String topic;
@@ -310,16 +312,19 @@ public class JobImpl implements Job, Comparable<JobImpl> {
         return Job.PROPERTY_JOB_PROGRESS_ETA;
     }
 
-    public String log(final String message, final Object... args) {
+    public String log(final int logMaxCount, final String message, final 
Object... args) {
+
+        if (logMaxCount <= 0) {
+            this.properties.remove(Job.PROPERTY_JOB_PROGRESS_LOG);
+            return Job.PROPERTY_JOB_PROGRESS_LOG;
+        }
+
         final String logEntry = MessageFormat.format(message, args);
         final String[] entries = 
this.getProperty(Job.PROPERTY_JOB_PROGRESS_LOG, String[].class);
         if ( entries == null ) {
             this.setProperty(Job.PROPERTY_JOB_PROGRESS_LOG, new String[] 
{logEntry});
         } else {
-            final String[] newEntries = new String[entries.length + 1];
-            System.arraycopy(entries, 0, newEntries, 0, entries.length);
-            newEntries[entries.length] = logEntry;
-            this.setProperty(Job.PROPERTY_JOB_PROGRESS_LOG, newEntries);
+            addLog(logEntry, entries, logMaxCount);
         }
         return Job.PROPERTY_JOB_PROGRESS_LOG;
     }
@@ -420,4 +425,23 @@ public class JobImpl implements Job, Comparable<JobImpl> {
         return "JobImpl [properties=" + properties + ", topic=" + topic
                 + ", path=" + path + ", jobId=" + jobId + "]";
     }
+
+    // helper methods
+    private void addLog(final String logEntry, final String[] entries, final 
int maxSize) {
+
+        final String[] newEntries;
+        if (entries.length >= maxSize) {
+            newEntries = new String[maxSize];
+            System.arraycopy(entries, entries.length - maxSize + 1, 
newEntries, 0, maxSize - 2);
+            final String prevLastLog = entries[entries.length - 1];
+            newEntries[maxSize - 2] = prevLastLog.endsWith(TRUNCATED_LOG) ? 
prevLastLog.replace(TRUNCATED_LOG, "") : prevLastLog;
+            newEntries[maxSize - 1] = logEntry.endsWith(TRUNCATED_LOG) ? 
logEntry : logEntry + TRUNCATED_LOG;
+        } else {
+            newEntries = new String[entries.length + 1];
+            System.arraycopy(entries, 0, newEntries, 0, entries.length);
+            newEntries[entries.length] = logEntry;
+
+        }
+        this.setProperty(Job.PROPERTY_JOB_PROGRESS_LOG, newEntries);
+    }
 }
diff --git 
a/src/main/java/org/apache/sling/event/impl/jobs/config/JobManagerConfiguration.java
 
b/src/main/java/org/apache/sling/event/impl/jobs/config/JobManagerConfiguration.java
index 1c267f6..a66d626 100644
--- 
a/src/main/java/org/apache/sling/event/impl/jobs/config/JobManagerConfiguration.java
+++ 
b/src/main/java/org/apache/sling/event/impl/jobs/config/JobManagerConfiguration.java
@@ -92,6 +92,12 @@ public class JobManagerConfiguration {
             description = "Specify the periodic interval in minutes (default 
is 48h - use 0 to disable) after which " +
                     "removed jobs (ERROR or DROPPED) should be cleaned from 
the repository.")
         int cleanup_period() default 2880;
+
+        @AttributeDefinition(name = "Progress Log message's max count",
+                description = "Max number of log messages that can stored by 
consumer to add information about current state of Job.\n" +
+                        "Any attempt to add more information would result into 
purging of the least recent messages." +
+                        "Use 0 to discard all the logs. default is -1 (to 
indicate infinite). ")
+        int progresslog_maxCount() default -1;
     }
     /** Logger. */
     private final Logger logger = 
LoggerFactory.getLogger("org.apache.sling.event.impl.jobs");
@@ -150,6 +156,11 @@ public class JobManagerConfiguration {
 
     private volatile long startupDelay;
 
+    /**
+     * The max count of job progress log messages
+     */
+    private int progressLogMaxCount;
+
     private volatile InitDelayingTopologyEventListener startupDelayListener;
 
     private volatile boolean disabledDistribution;
@@ -258,6 +269,13 @@ public class JobManagerConfiguration {
         // an immediate effect - it will only have an effect on next 
activation.
         // (as 'startup delay runnable' is already scheduled in activate)
         this.startupDelay = config.startup_delay();
+
+        if (config.progresslog_maxCount() < 0) {
+            this.progressLogMaxCount = Integer.MAX_VALUE;
+        } else {
+            this.progressLogMaxCount = config.progresslog_maxCount();
+        }
+
     }
 
     /**
@@ -413,6 +431,10 @@ public class JobManagerConfiguration {
         return this.jobsBasePathWithSlash;
     }
 
+    public int getProgressLogMaxCount() {
+        return this.progressLogMaxCount;
+    }
+
     public String getPreviousVersionAnonPath() {
         return this.previousVersionAnonPath;
     }
diff --git 
a/src/main/java/org/apache/sling/event/impl/jobs/queues/JobExecutionContextImpl.java
 
b/src/main/java/org/apache/sling/event/impl/jobs/queues/JobExecutionContextImpl.java
index d2c2286..2eb3465 100644
--- 
a/src/main/java/org/apache/sling/event/impl/jobs/queues/JobExecutionContextImpl.java
+++ 
b/src/main/java/org/apache/sling/event/impl/jobs/queues/JobExecutionContextImpl.java
@@ -86,7 +86,8 @@ public class JobExecutionContextImpl implements 
JobExecutionContext {
 
     @Override
     public void log(final String message, Object... args) {
-        handler.persistJobProperties(handler.getJob().log(message, args));
+        final int logMaxCount = handler.getProgressLogMaxCount();
+        handler.persistJobProperties(handler.getJob().log(logMaxCount, 
message, args));
     }
 
     @Override
diff --git a/src/test/java/org/apache/sling/event/impl/jobs/JobsImplTest.java 
b/src/test/java/org/apache/sling/event/impl/jobs/JobsImplTest.java
index 4a540b0..496ca4f 100644
--- a/src/test/java/org/apache/sling/event/impl/jobs/JobsImplTest.java
+++ b/src/test/java/org/apache/sling/event/impl/jobs/JobsImplTest.java
@@ -19,6 +19,8 @@
 package org.apache.sling.event.impl.jobs;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -34,7 +36,7 @@ public class JobsImplTest {
 
     @Test public void testSorting() {
         final Calendar now = Calendar.getInstance();
-        final Map<String, Object> properties = new HashMap<String, Object>();
+        final Map<String, Object> properties = new HashMap<>();
         properties.put(Job.PROPERTY_JOB_CREATED, now);
 
         final JobImpl job1 = new JobImpl("test", "hello_1", properties);
@@ -59,4 +61,100 @@ public class JobsImplTest {
         assertEquals(job5, list.get(4));
 
     }
+
+    @Test
+    public void testProgressLogCount() {
+        final Map<String, Object> properties = new HashMap<>();
+
+        final JobImpl job = new JobImpl("test", "hello_1", properties);
+
+        assertNull(job.getProperty(Job.PROPERTY_JOB_PROGRESS_LOG));
+        assertNull(job.getProgressLog());
+
+        for (int i = 0; i < 20; i++) {
+            job.log(10, "message_" + i);
+        }
+
+        final String[] progressLog = job.getProgressLog();
+        assertEquals(10, progressLog.length);
+        for (int i = 0; i < 9; i++) {
+            assertEquals("message_1" + i, progressLog[i]);
+        }
+        assertEquals("message_19" + JobImpl.TRUNCATED_LOG, progressLog[9]);
+    }
+
+    @Test
+    public void testProgressLogCountWithZeroCount() {
+        final Map<String, Object> properties = new HashMap<>();
+
+        final JobImpl job = new JobImpl("test", "hello_1", properties);
+
+        for (int i = 0; i < 2; i++) {
+            job.log(0, "message_" + i);
+        }
+
+        assertNull(job.getProgressLog());
+    }
+
+    @Test
+    public void testProgressLogCountWithNegativeCount() {
+        final Map<String, Object> properties = new HashMap<>();
+
+        final JobImpl job = new JobImpl("test", "hello_1", properties);
+
+        for (int i = 0; i < 2; i++) {
+            job.log(-2, "message_" + i);
+        }
+
+        assertNull(job.getProgressLog());
+    }
+
+    @Test
+    public void testProgressLogCountWithInfiniteCount() {
+        final Map<String, Object> properties = new HashMap<>();
+
+        final JobImpl job = new JobImpl("test", "hello_1", properties);
+
+        for (int i = 0; i < 20; i++) {
+            job.log(Integer.MAX_VALUE, "message_" + i);
+        }
+
+        final String[] progressLog = job.getProgressLog();
+        assertEquals(20, progressLog.length);
+        for (int i = 0; i < 20; i++) {
+            assertEquals("message_" + i, progressLog[i]);
+        }
+    }
+
+    @Test
+    public void testProgressLogCountMigrationFromOldJob() {
+        final Map<String, Object> properties = new HashMap<>();
+        properties.put(Job.PROPERTY_JOB_PROGRESS_LOG, new String[0]);
+
+        final JobImpl job = new JobImpl("test", "hello_1", properties);
+
+        assertTrue(job.getProperty(Job.PROPERTY_JOB_PROGRESS_LOG) instanceof 
String[]);
+
+        for (int i = 0; i < 20; i++) {
+            job.log(Integer.MAX_VALUE, "message_" + i);
+        }
+
+        final String[] progressLog = job.getProgressLog();
+        assertEquals(20, progressLog.length);
+        for (int i = 0; i < 20; i++) {
+            assertEquals("message_" + i, progressLog[i]);
+        }
+
+        // now create a new Job with Max Count
+        final JobImpl newJob = new JobImpl("test", "hello_1", properties);
+        for (int i = 0; i < 20; i++) {
+            newJob.log(10, "newMessage_" + i);
+        }
+        final String[] newProgressLog = newJob.getProgressLog();
+        assertEquals(10, newProgressLog.length);
+        for (int i = 0; i < 9; i++) {
+            assertEquals("newMessage_1" + i, newProgressLog[i]);
+        }
+        assertEquals("newMessage_19" + JobImpl.TRUNCATED_LOG, 
newProgressLog[9]);
+    }
 }
diff --git 
a/src/test/java/org/apache/sling/event/impl/jobs/config/JobManagerConfigurationTestFactory.java
 
b/src/test/java/org/apache/sling/event/impl/jobs/config/JobManagerConfigurationTestFactory.java
index 3ece2a3..24ffca7 100644
--- 
a/src/test/java/org/apache/sling/event/impl/jobs/config/JobManagerConfigurationTestFactory.java
+++ 
b/src/test/java/org/apache/sling/event/impl/jobs/config/JobManagerConfigurationTestFactory.java
@@ -52,7 +52,10 @@ public class JobManagerConfigurationTestFactory {
             public int cleanup_period() {
                 return 0;
             }
-            
+
+            public int progresslog_maxCount() {
+                return Integer.MAX_VALUE;
+            }
         });
         return real;
     }

Reply via email to