From 73287c389010386b4abe82ea251a61d97cecd7d8 Mon Sep 17 00:00:00 2001 From: Will Ezell Date: Fri, 20 Dec 2024 10:33:18 -0500 Subject: [PATCH 1/4] minor(memoryReport) make the free memory report what is left available to the jvm (#30948) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref: #30947 This now looks right, where `Free Memory = Total Memory - Filled Memory` ![Screenshot 2024-12-13 at 9 45 03 PM](https://github.com/user-attachments/assets/cba314d9-8ffb-4097-9e27-b6d5e7e90b67) --- .../ext/cmsmaintenance/cachestats_guava.jsp | 30 +++++++++++++------ .../cmsmaintenance/view_cms_maintenance.jsp | 19 ++++++++---- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/dotCMS/src/main/webapp/html/portlet/ext/cmsmaintenance/cachestats_guava.jsp b/dotCMS/src/main/webapp/html/portlet/ext/cmsmaintenance/cachestats_guava.jsp index 9368a093bd80..65734bb021a2 100644 --- a/dotCMS/src/main/webapp/html/portlet/ext/cmsmaintenance/cachestats_guava.jsp +++ b/dotCMS/src/main/webapp/html/portlet/ext/cmsmaintenance/cachestats_guava.jsp @@ -22,32 +22,44 @@ <%}%> + +<% + + long maxMemory = Runtime.getRuntime().maxMemory(); + long totalMemoryInUse = Runtime.getRuntime().totalMemory(); + long freeMemory = Runtime.getRuntime().freeMemory(); + long usedMemory = totalMemoryInUse - freeMemory; + long availableMemory = maxMemory - usedMemory; + +%> + +
- - - - - - - -
<%= LanguageUtil.get(pageContext, "Total-Memory-Available") %> + <%= LanguageUtil.get( pageContext, "Total-Memory-Available" ) %> / Xmx <%=UtilMethods.prettyByteify(Runtime.getRuntime().maxMemory())%> + <%=UtilMethods.prettyByteify( maxMemory )%>
<%= LanguageUtil.get(pageContext, "Memory-Allocated") %> + <%= LanguageUtil.get( pageContext, "Memory-Allocated" ) %> <%= UtilMethods.prettyByteify(Runtime.getRuntime().totalMemory())%> + <%= UtilMethods.prettyByteify( totalMemoryInUse )%>
<%= LanguageUtil.get(pageContext, "Filled-Memory") %> + <%= LanguageUtil.get( pageContext, "Filled-Memory" ) %> <%= UtilMethods.prettyByteify(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())%> + <%= UtilMethods.prettyByteify( usedMemory )%>
<%= LanguageUtil.get(pageContext, "Free-Memory") %> + <%= LanguageUtil.get( pageContext, "Free-Memory" ) %> <%= UtilMethods.prettyByteify(Runtime.getRuntime().freeMemory())%> + <%= UtilMethods.prettyByteify( availableMemory )%>
@@ -95,4 +107,4 @@ dojo.ready(function() { -
\ No newline at end of file + diff --git a/dotCMS/src/main/webapp/html/portlet/ext/cmsmaintenance/view_cms_maintenance.jsp b/dotCMS/src/main/webapp/html/portlet/ext/cmsmaintenance/view_cms_maintenance.jsp index b2695f06a601..5ee3f7470cb9 100644 --- a/dotCMS/src/main/webapp/html/portlet/ext/cmsmaintenance/view_cms_maintenance.jsp +++ b/dotCMS/src/main/webapp/html/portlet/ext/cmsmaintenance/view_cms_maintenance.jsp @@ -1295,31 +1295,40 @@ dd.leftdl {
+ <% + + long maxMemory = Runtime.getRuntime().maxMemory(); + long totalMemoryInUse = Runtime.getRuntime().totalMemory(); + long freeMemory = Runtime.getRuntime().freeMemory(); + long usedMemory = totalMemoryInUse - freeMemory; + long availableMemory = maxMemory - usedMemory; + + %>
- - - - -
<%= LanguageUtil.get( pageContext, "Total-Memory-Available" ) %> + <%= LanguageUtil.get( pageContext, "Total-Memory-Available" ) %> / Xmx <%=UtilMethods.prettyByteify( Runtime.getRuntime().maxMemory() )%> + <%=UtilMethods.prettyByteify( maxMemory )%>
<%= LanguageUtil.get( pageContext, "Memory-Allocated" ) %> <%= UtilMethods.prettyByteify( Runtime.getRuntime().totalMemory() )%> + <%= UtilMethods.prettyByteify( totalMemoryInUse )%>
<%= LanguageUtil.get( pageContext, "Filled-Memory" ) %> <%= UtilMethods.prettyByteify( Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory() )%> + <%= UtilMethods.prettyByteify( usedMemory )%>
<%= LanguageUtil.get( pageContext, "Free-Memory" ) %> <%= UtilMethods.prettyByteify( Runtime.getRuntime().freeMemory() )%> + <%= UtilMethods.prettyByteify( availableMemory )%>
From a2c70ab92d4764a1676f03a1a935868c791ca1e4 Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Fri, 20 Dec 2024 10:25:18 -0600 Subject: [PATCH 2/4] Fix (Core): Improving error handling on job processors discovery (#30996) This pull request includes changes to improve the job processor discovery mechanism in the `dotCMS` project. The most important changes involve refactoring the scope validation logic and adding comprehensive tests for the job processor discovery functionality. Refactoring and improvements: * [`dotCMS/src/main/java/com/dotcms/jobs/business/api/JobProcessorDiscovery.java`](diffhunk://#diff-796e7503dcb4b03e85602c13708c084a19b3b5555bb5bbc46667ebabca1dc9dfL52-R53): Refactored the `discoverJobProcessors` method to use the new `isValidateScope` method for scope validation instead of `validateScope`. This change improves readability and error handling by logging invalid scope errors instead of throwing exceptions. [[1]](diffhunk://#diff-796e7503dcb4b03e85602c13708c084a19b3b5555bb5bbc46667ebabca1dc9dfL52-R53) [[2]](diffhunk://#diff-796e7503dcb4b03e85602c13708c084a19b3b5555bb5bbc46667ebabca1dc9dfR75-R88) Testing enhancements: * [`dotcms-integration/src/test/java/com/dotcms/Junit5Suite1.java`](diffhunk://#diff-4b36fa8f2a871dca49e798d2b65b4f8a0c7ad264fd2fbb25f8bb435ff6dae160R3): Added `JobProcessorDiscoveryTest` to the JUnit test suite to ensure the new job processor discovery functionality is tested. [[1]](diffhunk://#diff-4b36fa8f2a871dca49e798d2b65b4f8a0c7ad264fd2fbb25f8bb435ff6dae160R3) [[2]](diffhunk://#diff-4b36fa8f2a871dca49e798d2b65b4f8a0c7ad264fd2fbb25f8bb435ff6dae160L19-R21) * [`dotcms-integration/src/test/java/com/dotcms/jobs/business/api/JobProcessorDiscoveryTest.java`](diffhunk://#diff-b9c6f725701a5ba5108e37e1b740bee7ce75b2dc30b5e4b39f7d242f64559a5eR1-R170): Created a new test class to verify the CDI-based job processor discovery functionality. This class includes tests for discovering valid job processors, handling invalid scope processors, ignoring non-processor classes, and managing empty results. --- .../business/api/JobProcessorDiscovery.java | 21 ++- .../test/java/com/dotcms/Junit5Suite1.java | 4 +- .../api/JobProcessorDiscoveryTest.java | 170 ++++++++++++++++++ 3 files changed, 185 insertions(+), 10 deletions(-) create mode 100644 dotcms-integration/src/test/java/com/dotcms/jobs/business/api/JobProcessorDiscoveryTest.java diff --git a/dotCMS/src/main/java/com/dotcms/jobs/business/api/JobProcessorDiscovery.java b/dotCMS/src/main/java/com/dotcms/jobs/business/api/JobProcessorDiscovery.java index c6e79bb18cb3..9ec5565ff5e8 100644 --- a/dotCMS/src/main/java/com/dotcms/jobs/business/api/JobProcessorDiscovery.java +++ b/dotCMS/src/main/java/com/dotcms/jobs/business/api/JobProcessorDiscovery.java @@ -49,11 +49,8 @@ public List> discoverJobProcessors() { for (Bean bean : beans) { Class beanClass = bean.getBeanClass(); - if (JobProcessor.class.isAssignableFrom(beanClass)) { - - // Validate that the bean is in the correct scope - validateScope(bean); - + if (JobProcessor.class.isAssignableFrom(beanClass) + && isValidateScope(bean)) { processors.add((Class) beanClass); Logger.debug(this, "Discovered JobProcessor: " + beanClass.getName()); } @@ -75,14 +72,20 @@ public List> discoverJobProcessors() { * Validates that the scope of the bean is correct for a JobProcessor. * * @param bean The bean to validate. + * @return True if the scope is valid, false otherwise. */ - private void validateScope(Bean bean) { + private boolean isValidateScope(Bean bean) { + Class scope = bean.getScope(); if (scope != Dependent.class) { - throw new DotRuntimeException( - "JobProcessor " + bean.getBeanClass().getName() + - " must use @Dependent scope, found: " + scope.getName()); + final String errorMessage = "JobProcessor " + bean.getBeanClass().getName() + + " must use @Dependent scope, found: " + scope.getName() + " scope. " + + "Won't be registered."; + Logger.error(this, errorMessage); + return false; } + + return true; } } \ No newline at end of file diff --git a/dotcms-integration/src/test/java/com/dotcms/Junit5Suite1.java b/dotcms-integration/src/test/java/com/dotcms/Junit5Suite1.java index 32210b2167eb..2e9375622e65 100644 --- a/dotcms-integration/src/test/java/com/dotcms/Junit5Suite1.java +++ b/dotcms-integration/src/test/java/com/dotcms/Junit5Suite1.java @@ -1,5 +1,6 @@ package com.dotcms; +import com.dotcms.jobs.business.api.JobProcessorDiscoveryTest; import com.dotcms.jobs.business.api.JobQueueManagerAPICDITest; import com.dotcms.jobs.business.api.JobQueueManagerAPIIntegrationTest; import com.dotcms.jobs.business.processor.impl.ImportContentletsProcessorIntegrationTest; @@ -16,7 +17,8 @@ JobQueueManagerAPIIntegrationTest.class, JobQueueHelperIntegrationTest.class, ImportContentletsProcessorIntegrationTest.class, - ContentImportResourceIntegrationTest.class + ContentImportResourceIntegrationTest.class, + JobProcessorDiscoveryTest.class }) public class Junit5Suite1 { diff --git a/dotcms-integration/src/test/java/com/dotcms/jobs/business/api/JobProcessorDiscoveryTest.java b/dotcms-integration/src/test/java/com/dotcms/jobs/business/api/JobProcessorDiscoveryTest.java new file mode 100644 index 000000000000..e4f71defe355 --- /dev/null +++ b/dotcms-integration/src/test/java/com/dotcms/jobs/business/api/JobProcessorDiscoveryTest.java @@ -0,0 +1,170 @@ +package com.dotcms.jobs.business.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.dotcms.jobs.business.job.Job; +import com.dotcms.jobs.business.processor.JobProcessor; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Any; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Test class for verifying the CDI-based job processor discovery functionality in + * JobProcessorDiscovery. + *

+ * This class tests the automatic discovery of JobProcessor implementations through CDI, ensuring: + *

    + *
  • Only JobProcessor implementations with @Dependent scope are discovered
  • + *
  • Invalid scope processors (non-@Dependent) are filtered out
  • + *
  • Non-JobProcessor classes are ignored
  • + *
  • Empty results are handled correctly
  • + *
+ */ +public class JobProcessorDiscoveryTest { + + private BeanManager beanManager; + private JobProcessorDiscovery discovery; + + @BeforeEach + void setUp() { + beanManager = mock(BeanManager.class); + discovery = new JobProcessorDiscovery(beanManager); + } + + /** + * Method to test: discoverJobProcessors + * Given Scenario: Multiple classes are available, including valid JobProcessors, + * invalid scope processors, and non-processor classes + * ExpectedResult: Only valid JobProcessor implementations with @Dependent scope are discovered + */ + @Test + void test_discover_valid_job_processors() { + + // Create mock beans + Bean validBean1 = createMockBean(ValidJobProcessor1.class, Dependent.class); + Bean validBean2 = createMockBean(ValidJobProcessor2.class, Dependent.class); + Bean invalidScopeBean = createMockBean( + InvalidScopeProcessor.class, ApplicationScoped.class); + Bean nonProcessorBean = createMockBean(NonProcessor.class, Dependent.class); + + // Set up bean manager to return our mock beans + Set> beans = new HashSet<>(); + beans.add(validBean1); + beans.add(validBean2); + beans.add(invalidScopeBean); + beans.add(nonProcessorBean); + when(beanManager.getBeans(JobProcessor.class, Any.Literal.INSTANCE)).thenReturn(beans); + + // Test discovery + List> discovered = discovery.discoverJobProcessors(); + + // Verify results + assertEquals(2, discovered.size(), + "Should discover only valid JobProcessor implementations"); + assertTrue(discovered.contains(ValidJobProcessor1.class), + "Should discover ValidJobProcessor1"); + assertTrue(discovered.contains(ValidJobProcessor2.class), + "Should discover ValidJobProcessor2"); + } + + /** + * Method to test: discoverJobProcessors + * Given Scenario: No JobProcessor implementations are available + * ExpectedResult: An empty list is returned + */ + @Test + void test_discover_no_processors() { + + // Set up bean manager to return empty set + when(beanManager.getBeans(JobProcessor.class, Any.Literal.INSTANCE)) + .thenReturn(new HashSet<>()); + + // Test discovery + List> discovered = discovery.discoverJobProcessors(); + + // Verify results + assertTrue(discovered.isEmpty(), "Should return empty list when no processors found"); + } + + /** + * Helper method to create mock beans for testing. + * + * @param beanClass The class of the bean to mock + * @param scope The scope annotation class to apply to the bean + * @return A mock Bean instance + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + private Bean createMockBean(Class beanClass, Class scope) { + Bean bean = mock(Bean.class); + when(bean.getBeanClass()).thenReturn(beanClass); + when(bean.getScope()).thenReturn(scope); + return bean; + } + + /** + * Valid test JobProcessor implementation with correct @Dependent scope. + */ + @Dependent + static class ValidJobProcessor1 implements JobProcessor { + + @Override + public void process(Job job) { + } + + @Override + public Map getResultMetadata(Job job) { + return null; + } + } + + /** + * Second valid test JobProcessor implementation with correct @Dependent scope. + */ + @Dependent + static class ValidJobProcessor2 implements JobProcessor { + + @Override + public void process(Job job) { + } + + @Override + public Map getResultMetadata(Job job) { + return null; + } + } + + /** + * Invalid test JobProcessor implementation with incorrect @ApplicationScoped scope. + */ + @ApplicationScoped + static class InvalidScopeProcessor implements JobProcessor { + + @Override + public void process(Job job) { + } + + @Override + public Map getResultMetadata(Job job) { + return null; + } + } + + /** + * Test class that doesn't implement JobProcessor interface. + */ + @Dependent + static class NonProcessor { + // Not a JobProcessor implementation + } +} \ No newline at end of file From a5315a41f2f64a83f54a59ebcee3cf4070a8e9ba Mon Sep 17 00:00:00 2001 From: Rafael Velazco Date: Fri, 20 Dec 2024 12:41:27 -0400 Subject: [PATCH 3/4] fix(UVE): Ensure Window Object Re-Initialization in UVE iframe (#30989) This pull request includes several changes aimed at improving the functionality and testing of the `EditEmaEditorComponent` in the `core-web` library. The key updates involve refactoring the iframe URL handling, enhancing the test coverage, and improving the inline editing feature. ### Refactoring and Improvements: * Updated the iframe `src` binding to use `uveStore.$iframeURL()` instead of `$editorProps().iframe.src` in `edit-ema-editor.component.html`. * Refactored the `setIframeContent` method to include an `enableInlineEdit` parameter and moved the inline editing script handling to a new method `handleInlineScripts` in `edit-ema-editor.component.ts`. [[1]](diffhunk://#diff-24dc496db1eb6feb3e10a031baa4b4b63c20f1cd02ade574065f6b967f11c365L195-L215) [[2]](diffhunk://#diff-24dc496db1eb6feb3e10a031baa4b4b63c20f1cd02ade574065f6b967f11c365L685-R718) ### Testing Enhancements: * Added event listener and dispatcher methods to the iframe mock in `edit-ema-editor.component.spec.ts` to simulate iframe load events. * Modified multiple test cases to dispatch iframe load events and verify the iframe content in `edit-ema-editor.component.spec.ts`. [[1]](diffhunk://#diff-34ddc5fbacaf04b962f2037385ed284310d5faf35ba409d5705b2caadd5d796aL2617-R2625) [[2]](diffhunk://#diff-34ddc5fbacaf04b962f2037385ed284310d5faf35ba409d5705b2caadd5d796aR2650-R2652) [[3]](diffhunk://#diff-34ddc5fbacaf04b962f2037385ed284310d5faf35ba409d5705b2caadd5d796aL2668-R2686) [[4]](diffhunk://#diff-34ddc5fbacaf04b962f2037385ed284310d5faf35ba409d5705b2caadd5d796aR2826-R2842) [[5]](diffhunk://#diff-34ddc5fbacaf04b962f2037385ed284310d5faf35ba409d5705b2caadd5d796aR2858-R2860) ### Store and State Management: * Introduced a new computed property `$iframeURL` in the `withEditor` store to dynamically generate iframe URLs, including handling traditional pages by returning `about:blank`. [[1]](diffhunk://#diff-86e692578757ed7f4f6cba5d0aeb07641312f3b17885825d1a45987153ae87f0L39-R61) [[2]](diffhunk://#diff-86e692578757ed7f4f6cba5d0aeb07641312f3b17885825d1a45987153ae87f0L124-L127) [[3]](diffhunk://#diff-86e692578757ed7f4f6cba5d0aeb07641312f3b17885825d1a45987153ae87f0L140-L141) [[4]](diffhunk://#diff-86e692578757ed7f4f6cba5d0aeb07641312f3b17885825d1a45987153ae87f0L150) [[5]](diffhunk://#diff-86e692578757ed7f4f6cba5d0aeb07641312f3b17885825d1a45987153ae87f0R193-R203) * Removed the `src` property from the `EditorProps` interface as it is now handled by the computed `$iframeURL`. ### Code Cleanup: * Removed unnecessary `requestAnimationFrame` calls and related code from `edit-ema-editor.component.ts`. [[1]](diffhunk://#diff-24dc496db1eb6feb3e10a031baa4b4b63c20f1cd02ade574065f6b967f11c365L195-L215) [[2]](diffhunk://#diff-24dc496db1eb6feb3e10a031baa4b4b63c20f1cd02ade574065f6b967f11c365L685-R718) * Cleaned up unused imports and reorganized utility function usage in `withEditor.ts`. These changes collectively enhance the maintainability, functionality, and testability of the `EditEmaEditorComponent`. ### Video https://github.com/user-attachments/assets/2d020e92-0dd3-4b80-8bb4-028c5a8b252a --- .../edit-ema-editor.component.html | 2 +- .../edit-ema-editor.component.spec.ts | 40 +++++---- .../edit-ema-editor.component.ts | 81 +++++++++++-------- .../src/lib/store/features/editor/models.ts | 1 - .../store/features/editor/withEditor.spec.ts | 27 ++++--- .../lib/store/features/editor/withEditor.ts | 36 ++++++--- .../src/lib/store/features/load/withLoad.ts | 1 - 7 files changed, 119 insertions(+), 69 deletions(-) diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html index fa44544ad286..1f734b06cd29 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html @@ -29,7 +29,7 @@ class="iframe-wrapper">