Skip to content

Migrate to DownloadHandler API for Vaadin 25 compatibility#189

Open
mlopezFC wants to merge 3 commits into
masterfrom
issue-179
Open

Migrate to DownloadHandler API for Vaadin 25 compatibility#189
mlopezFC wants to merge 3 commits into
masterfrom
issue-179

Conversation

@mlopezFC
Copy link
Copy Markdown
Member

@mlopezFC mlopezFC commented Nov 25, 2025

This PR introduces support for the new DownloadHandler API while maintaining full backward compatibility with existing code. The deprecated StreamResource API methods are preserved but marked for removal in version 3.0.0, allowing users to migrate at their own pace. All export formats (Excel, DOCX, PDF, CSV) now have corresponding DownloadHandler-based methods alongside the existing StreamResource methods.

Closes #179

Summary by CodeRabbit

  • New Features

    • New DownloadHandler-based export API for Excel, PDF, DOCX and CSV with attachable handler objects and component binding.
  • Documentation

    • Added Migration Guide and README section with examples, timelines, Vaadin requirement, compatibility matrix and troubleshooting.
  • Deprecations

    • Legacy StreamResource export methods deprecated (backward compatible until v3.0.0).
  • Refactor

    • Centralized concurrency/throttling and adapter layers to unify concurrent export handling and simplify implementations.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 25, 2025

Review Change Stack

Walkthrough

Replaces StreamResource export APIs with DownloadHandler-based handlers, centralizes concurrent-download throttling in ConcurrentOperationBase, adds adapters and refactors concurrent writers, exposes new GridExporter get*DownloadHandler() methods and rewires footer anchors, and adds migration documentation and demo updates.

Changes

DownloadHandler API Migration

Layer / File(s) Summary
Shared concurrency infrastructure
src/main/java/.../ConcurrentOperationBase.java
ConcurrentOperationBase centralizes semaphore-based throttling, cost conversion, global limit API, lifecycle callbacks, and runWithSemaphore orchestration.
DownloadHandler adapter bridges
src/main/java/.../StreamResourceWriterAdapter.java, src/main/java/.../ConcurrentDownloadHandler.java
StreamResourceWriterAdapter adapts StreamResourceWriter to Vaadin DownloadHandler; ConcurrentDownloadHandler applies concurrency control to DownloadHandler delegates via runWithSemaphore.
Refactor ConcurrentStreamResourceWriter
src/main/java/.../ConcurrentStreamResourceWriter.java
ConcurrentStreamResourceWriter now extends ConcurrentOperationBase, forwards limit APIs to the base, and simplifies accept to a runWithSemaphore wrapper around the delegate writer.
GridExporter DownloadHandler API and button rewiring
src/main/java/.../GridExporter.java
Adds public getExcelDownloadHandler(), getPdfDownloadHandler(), getDocxDownloadHandler(), getCsvDownloadHandler() (template overloads where applicable); introduces GridExporterConcurrentDownloadHandler, makeConcurrentDownloadHandler, and rewires auto-attached footer Anchor buttons to bind handlers via forComponent.
Migration documentation and demo
MIGRATION_GUIDE.md, README.md, src/test/java/.../GridExporterCustomLinkDemo.java
Adds MIGRATION_GUIDE.md and README section describing method mappings, Vaadin 24.8.0+ requirement, timelines, examples, troubleshooting; demo updated to use getExcelDownloadHandler().

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • javier-godoy
  • paodb
  • scardanzan
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: migrating the codebase from deprecated StreamResource to DownloadHandler API for Vaadin 25 compatibility, which aligns with the primary objective.
Linked Issues check ✅ Passed The PR successfully addresses issue #179 by introducing DownloadHandler-based methods for all export formats (Excel, DOCX, PDF, CSV), maintaining backward compatibility with deprecated StreamResource methods, and ensuring Vaadin 25 compatibility.
Out of Scope Changes check ✅ Passed All changes are in scope: documentation updates provide migration guidance, new DownloadHandler implementations replace/supplement StreamResource, concurrency handling is refactored appropriately, and test updates demonstrate the new API usage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-179

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java (1)

50-54: Add null check for writer parameter.

The writer parameter should be validated to prevent potential NPE. While the adapter pattern delegates resource management appropriately, defensive validation improves robustness.

Apply this diff to add validation:

 public StreamResourceWriterAdapter(StreamResourceWriter writer, String filename, String contentType) {
+    this.writer = Objects.requireNonNull(writer, "writer cannot be null");
-    this.writer = writer;
     this.filename = filename;
     this.contentType = contentType;
 }
src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java (1)

617-621: Use pattern matching for instanceof.

Since the project targets Java 17, you can simplify the instanceof check using pattern matching.

Apply this diff:

 private void setButtonEnabled(boolean enabled) {
-  if (button instanceof HasEnabled) {
-    grid.getUI().ifPresent(ui -> ui.access(() -> ((HasEnabled) button).setEnabled(enabled)));
+  if (button instanceof HasEnabled hasEnabled) {
+    grid.getUI().ifPresent(ui -> ui.access(() -> hasEnabled.setEnabled(enabled)));
   }
 }
src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java (1)

28-28: Remove unused import.

The java.io.OutputStream import is not used in this file.

Apply this diff:

 import java.io.IOException;
 import java.io.InterruptedIOException;
-import java.io.OutputStream;
 import java.nio.channels.InterruptedByTimeoutException;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b82e6d and d9b1b9f.

📒 Files selected for processing (7)
  • MIGRATION_GUIDE.md (1 hunks)
  • README.md (1 hunks)
  • pom.xml (1 hunks)
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java (5 hunks)
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java (1 hunks)
  • src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterCustomLinkDemo.java (2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 119
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/CsvStreamResourceWriter.java:77-93
Timestamp: 2024-07-27T22:14:37.039Z
Learning: User javier-godoy requested to open a follow-up GitHub issue to discuss resource management in the refactored code of CsvStreamResourceWriter, highlighting the absence of try-with-resources in the previous implementation.
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 119
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/CsvStreamResourceWriter.java:77-93
Timestamp: 2024-10-08T21:28:24.972Z
Learning: User javier-godoy requested to open a follow-up GitHub issue to discuss resource management in the refactored code of CsvStreamResourceWriter, highlighting the absence of try-with-resources in the previous implementation.
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:315-315
Timestamp: 2024-10-08T21:28:24.972Z
Learning: In the `GridExporter` class, CSV exports are not subject to concurrent controls, so `getCsvStreamResource` returns a `StreamResource` instead of a `GridExporterStreamResource`.
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:315-315
Timestamp: 2024-09-26T18:26:03.345Z
Learning: In the `GridExporter` class, CSV exports are not subject to concurrent controls, so `getCsvStreamResource` returns a `StreamResource` instead of a `GridExporterStreamResource`.
📚 Learning: 2024-10-08T21:28:24.972Z
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:315-315
Timestamp: 2024-10-08T21:28:24.972Z
Learning: In the `GridExporter` class, CSV exports are not subject to concurrent controls, so `getCsvStreamResource` returns a `StreamResource` instead of a `GridExporterStreamResource`.

Applied to files:

  • MIGRATION_GUIDE.md
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java
  • src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterCustomLinkDemo.java
📚 Learning: 2024-06-09T06:20:09.702Z
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 125
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:338-402
Timestamp: 2024-06-09T06:20:09.702Z
Learning: Zero or negative costs in the `setConcurrentDownloadCost` method do not require any permits, allowing such downloads to bypass the semaphore mechanism.

Applied to files:

  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java
📚 Learning: 2024-10-28T17:35:31.077Z
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 154
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/BaseStreamResourceWriter.java:98-103
Timestamp: 2024-10-28T17:35:31.077Z
Learning: `BaseStreamResourceWriter` and its extending classes are not public API currently, but the return type must be refactored as we are going to expose `BaseStreamResourceWriter` in the near future.

Applied to files:

  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java
🧬 Code graph analysis (1)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java (1)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterConcurrentSettings.java (1)
  • GridExporterConcurrentSettings (37-123)
🪛 GitHub Check: SonarCloud Code Analysis
src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java

[warning] 333-333: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2a&open=AZq84AEOvK_9d0Mfzo2a&pullRequest=189


[warning] 388-388: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2e&open=AZq84AEOvK_9d0Mfzo2e&pullRequest=189


[warning] 347-347: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2b&open=AZq84AEOvK_9d0Mfzo2b&pullRequest=189


[warning] 402-402: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2f&open=AZq84AEOvK_9d0Mfzo2f&pullRequest=189


[warning] 319-319: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2Z&open=AZq84AEOvK_9d0Mfzo2Z&pullRequest=189


[warning] 375-375: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2d&open=AZq84AEOvK_9d0Mfzo2d&pullRequest=189


[warning] 361-361: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2c&open=AZq84AEOvK_9d0Mfzo2c&pullRequest=189


[warning] 618-618: Replace this instanceof check and cast with 'instanceof HasEnabled hasenabled'

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2g&open=AZq84AEOvK_9d0Mfzo2g&pullRequest=189

src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java

[warning] 28-28: Remove this unused import 'java.io.OutputStream'.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AHAvK_9d0Mfzo2h&open=AZq84AHAvK_9d0Mfzo2h&pullRequest=189

🪛 LanguageTool
MIGRATION_GUIDE.md

[style] ~146-~146: Try using a more formal synonym for ‘check’.
Context: ...any issues during migration, please: 1. Check that you're using Vaadin 24.8.0 or late...

(VERIFY)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-vaadin24
🔇 Additional comments (14)
pom.xml (1)

7-7: LGTM!

The version updates correctly reflect the new feature set (DownloadHandler API) and required Vaadin dependency.

Also applies to: 13-13

README.md (1)

57-83: LGTM!

The migration documentation is clear, comprehensive, and provides helpful quick-start guidance for users upgrading to the new API.

src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterCustomLinkDemo.java (1)

79-79: LGTM!

The test correctly demonstrates migration to the new getExcelDownloadHandler() API.

MIGRATION_GUIDE.md (1)

1-189: LGTM!

The migration guide is comprehensive, well-structured, and provides clear before/after examples for all export formats.

src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java (5)

226-232: LGTM!

Formatting improvement with no functional changes.


310-405: LGTM!

The deprecation strategy is well-documented with clear migration paths to the new DownloadHandler-based methods. The removal timeline (version 3.0.0) provides adequate time for users to migrate.


406-489: LGTM!

The new DownloadHandler-based methods are correctly implemented. CSV export appropriately uses StreamResourceWriterAdapter directly without concurrent controls, consistent with the existing design where CSV exports are not throttled. Based on learnings, CSV exports are intentionally not subject to concurrent controls.


496-500: LGTM!

The helper method cleanly composes the adapter and concurrent handler layers.


570-634: LGTM!

The GridExporterConcurrentDownloadHandler correctly implements the concurrency framework with appropriate lifecycle hooks and UI association.

src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java (5)

46-58: LGTM!

The constants and static fields are well-defined for the global concurrency control mechanism.


60-90: LGTM!

The ConfigurableSemaphore correctly manages dynamic permit allocation with proper synchronization.


103-137: LGTM!

The limit management correctly handles enabling/disabling the semaphore and validates input parameters.


139-148: LGTM!

The cost-to-permits conversion correctly implements fixed-point arithmetic for fractional costs and properly handles zero/negative costs to bypass the semaphore. Based on learnings, this aligns with the documented behavior where non-positive costs require no permits.


277-315: LGTM!

The download handling logic correctly implements:

  • Lifecycle callbacks (onAccept/onFinish)
  • Permit acquisition with timeout
  • UI change detection
  • Proper resource cleanup in finally blocks

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java (1)

176-185: Fix indentation and spacing consistency.

The if statement on line 178 has inconsistent indentation (appears to be misaligned with the surrounding code) and missing spaces around the != operator.

           if (semaphore.tryAcquire(permits, getTimeout(), TimeUnit.NANOSECONDS)) {
             try {
-            if (ui != null && getAttachedUI()!=ui) {
-                // The UI has changed or was detached after acquirig the semaphore
-                throw new IOException("Detached UI");
-              }
+              if (ui != null && getAttachedUI() != ui) {
+                // The UI has changed or was detached after acquiring the semaphore
+                throw new IOException("Detached UI");
+              }
               delegate.accept(stream, session);
             } finally {
               semaphore.release(permits);

Note: There's also a typo "acquirig" → "acquiring" in the comment.

src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java (1)

56-64: Minor Javadoc inconsistency: "Sets" should be "Returns".

The Javadoc at line 57 says "Sets the timeout" but this is a getter method that returns the timeout value.

     /**
-     * Sets the timeout for acquiring a permit to start a download when there are
+     * Returns the timeout for acquiring a permit to start a download when there are
      * not enough permits
      * available in the semaphore.
      *
      * @see GridExporter#setConcurrentDownloadTimeout(long, TimeUnit)
      * @return the timeout in nanoseconds.
      */
     public abstract long getTimeout();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9b1b9f and 41a3440.

📒 Files selected for processing (3)
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java (2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 119
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/CsvStreamResourceWriter.java:77-93
Timestamp: 2024-07-27T22:14:37.039Z
Learning: User javier-godoy requested to open a follow-up GitHub issue to discuss resource management in the refactored code of CsvStreamResourceWriter, highlighting the absence of try-with-resources in the previous implementation.
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 119
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/CsvStreamResourceWriter.java:77-93
Timestamp: 2024-10-08T21:28:24.972Z
Learning: User javier-godoy requested to open a follow-up GitHub issue to discuss resource management in the refactored code of CsvStreamResourceWriter, highlighting the absence of try-with-resources in the previous implementation.
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:315-315
Timestamp: 2024-10-08T21:28:24.972Z
Learning: In the `GridExporter` class, CSV exports are not subject to concurrent controls, so `getCsvStreamResource` returns a `StreamResource` instead of a `GridExporterStreamResource`.
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:315-315
Timestamp: 2024-09-26T18:26:03.345Z
Learning: In the `GridExporter` class, CSV exports are not subject to concurrent controls, so `getCsvStreamResource` returns a `StreamResource` instead of a `GridExporterStreamResource`.
📚 Learning: 2024-10-08T21:28:24.972Z
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:315-315
Timestamp: 2024-10-08T21:28:24.972Z
Learning: In the `GridExporter` class, CSV exports are not subject to concurrent controls, so `getCsvStreamResource` returns a `StreamResource` instead of a `GridExporterStreamResource`.

Applied to files:

  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java
📚 Learning: 2024-10-28T17:35:31.077Z
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 154
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/BaseStreamResourceWriter.java:98-103
Timestamp: 2024-10-28T17:35:31.077Z
Learning: `BaseStreamResourceWriter` and its extending classes are not public API currently, but the return type must be refactored as we are going to expose `BaseStreamResourceWriter` in the near future.

Applied to files:

  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java
📚 Learning: 2024-06-09T06:20:09.702Z
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 125
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:338-402
Timestamp: 2024-06-09T06:20:09.702Z
Learning: Zero or negative costs in the `setConcurrentDownloadCost` method do not require any permits, allowing such downloads to bypass the semaphore mechanism.

Applied to files:

  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java
🧬 Code graph analysis (1)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java (2)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java (1)
  • SuppressWarnings (40-212)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java (1)
  • SuppressWarnings (42-200)
🪛 GitHub Check: SonarCloud Code Analysis
src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java

[warning] 35-35: Add a private constructor to hide the implicit public one.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq9YdoTZqhFnrDkgnFV&open=AZq9YdoTZqhFnrDkgnFV&pullRequest=189

🔇 Additional comments (8)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java (1)

43-53: LGTM! Clean delegation to the base class.

The refactoring to extend ConcurrentOperationBase and delegate the static setLimit/getLimit methods centralizes the concurrency control logic effectively. This aligns well with the new ConcurrentDownloadHandler that will share the same base.

src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java (5)

35-44: Abstract class - SonarCloud warning is a false positive.

Since ConcurrentOperationBase is declared abstract, it cannot be instantiated directly, and subclasses will provide their own constructors. The SonarCloud suggestion to add a private constructor doesn't apply here.


113-121: Minor: Potential TOCTOU between enabled check and synchronized read.

The enabled volatile check on line 114 and the synchronized block on lines 115-117 are not atomic. If another thread calls setLimit(POSITIVE_INFINITY) between these, the returned value may be stale. However, since getLimit() is a status/monitoring method and not used for critical decisions, this is acceptable.


45-74: LGTM! Well-designed configurable semaphore.

The ConfigurableSemaphore correctly uses reducePermits for decreasing and release for increasing, maintaining consistency. The toString() provides helpful debugging output with the fixed-point representation.


123-132: LGTM! Cost-to-permits conversion correctly handles edge cases.

The implementation properly handles zero/negative costs (returning 0 permits to bypass the semaphore), caps at MAX_COST, and ensures any positive cost requires at least 1 permit. This aligns with the established behavior where zero or negative costs bypass the semaphore mechanism. Based on learnings, this is the expected behavior.


87-100: LGTM! Proper limit configuration with infinity handling.

The method correctly validates input, handles POSITIVE_INFINITY to disable the semaphore, and uses synchronization when updating permits. The guard against zero/negative values prevents misconfiguration.

src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java (2)

40-54: LGTM! Clean parallel implementation to ConcurrentStreamResourceWriter.

The class structure mirrors ConcurrentStreamResourceWriter appropriately, extending ConcurrentOperationBase and wrapping a delegate DownloadHandler. This provides consistent concurrency control for the new DownloadHandler API.


171-210: LGTM! Correct concurrency-controlled download handling.

The handleDownloadRequest implementation correctly:

  • Invokes onAccept() before acquiring permits
  • Bypasses semaphore when enabled is false
  • Computes permits from cost within synchronized block
  • Checks for UI detachment after permit acquisition (when failOnUiChange is set)
  • Guarantees onFinish() via finally block
  • Properly handles InterruptedException by preserving interrupt status

The implementation is consistent with ConcurrentStreamResourceWriter.accept(), ensuring uniform behavior across both APIs.

@mlopezFC mlopezFC marked this pull request as draft November 26, 2025 11:16
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
8.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java (1)

22-27: Remove unused import.

The VaadinSession import on line 24 is not used in this file. The session is obtained from event.getSession() directly.

 import com.vaadin.flow.server.streams.DownloadHandler;
 import com.vaadin.flow.server.streams.DownloadEvent;
-import com.vaadin.flow.server.VaadinSession;
 import java.io.IOException;
 import java.io.InterruptedIOException;
 import java.nio.channels.InterruptedByTimeoutException;
src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java (1)

44-50: Consider documenting the volatile fields.

The enabled and failOnUiChange fields are volatile, which provides visibility guarantees but not atomicity for compound operations. The current usage appears safe since enabled is only set within synchronized blocks or checked before synchronized access. However, adding brief comments explaining the concurrency semantics would improve maintainability.

src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java (1)

1042-1079: Consider addressing the wildcard type if feasible.

The GridExporter<?> return type from getExporter() (line 1043) triggers a static analysis warning. While this is a private interface and the impact is limited, the wildcard is used because inner classes reference the enclosing GridExporter<T> which could potentially be typed more specifically.

Since this is internal to the class and the wildcard doesn't escape to public API, this is a minor concern that can be deferred.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 41a3440 and e608b7c.

📒 Files selected for processing (4)
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java (1 hunks)
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java (2 hunks)
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java (5 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 119
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/CsvStreamResourceWriter.java:77-93
Timestamp: 2024-10-08T21:28:24.972Z
Learning: User javier-godoy requested to open a follow-up GitHub issue to discuss resource management in the refactored code of CsvStreamResourceWriter, highlighting the absence of try-with-resources in the previous implementation.
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 119
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/CsvStreamResourceWriter.java:77-93
Timestamp: 2024-07-27T22:14:37.039Z
Learning: User javier-godoy requested to open a follow-up GitHub issue to discuss resource management in the refactored code of CsvStreamResourceWriter, highlighting the absence of try-with-resources in the previous implementation.
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:315-315
Timestamp: 2024-09-26T18:26:03.345Z
Learning: In the `GridExporter` class, CSV exports are not subject to concurrent controls, so `getCsvStreamResource` returns a `StreamResource` instead of a `GridExporterStreamResource`.
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:315-315
Timestamp: 2024-10-08T21:28:24.972Z
Learning: In the `GridExporter` class, CSV exports are not subject to concurrent controls, so `getCsvStreamResource` returns a `StreamResource` instead of a `GridExporterStreamResource`.
📚 Learning: 2024-09-26T18:26:03.345Z
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 143
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:315-315
Timestamp: 2024-09-26T18:26:03.345Z
Learning: In the `GridExporter` class, CSV exports are not subject to concurrent controls, so `getCsvStreamResource` returns a `StreamResource` instead of a `GridExporterStreamResource`.

Applied to files:

  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java
📚 Learning: 2024-10-28T17:35:31.077Z
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 154
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/BaseStreamResourceWriter.java:98-103
Timestamp: 2024-10-28T17:35:31.077Z
Learning: `BaseStreamResourceWriter` and its extending classes are not public API currently, but the return type must be refactored as we are going to expose `BaseStreamResourceWriter` in the near future.

Applied to files:

  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java
📚 Learning: 2024-07-27T22:14:37.039Z
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 119
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/CsvStreamResourceWriter.java:77-93
Timestamp: 2024-07-27T22:14:37.039Z
Learning: User javier-godoy requested to open a follow-up GitHub issue to discuss resource management in the refactored code of CsvStreamResourceWriter, highlighting the absence of try-with-resources in the previous implementation.

Applied to files:

  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java
📚 Learning: 2024-10-08T21:28:24.972Z
Learnt from: javier-godoy
Repo: FlowingCode/GridExporterAddon PR: 125
File: src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java:338-402
Timestamp: 2024-10-08T21:28:24.972Z
Learning: Zero or negative costs in the `setConcurrentDownloadCost` method do not require any permits, allowing such downloads to bypass the semaphore mechanism.

Applied to files:

  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java
🧬 Code graph analysis (1)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java (1)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterConcurrentSettings.java (1)
  • GridExporterConcurrentSettings (37-123)
🪛 GitHub Check: SonarCloud Code Analysis
src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java

[warning] 319-319: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2Z&open=AZq84AEOvK_9d0Mfzo2Z&pullRequest=189


[warning] 375-375: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2d&open=AZq84AEOvK_9d0Mfzo2d&pullRequest=189


[warning] 402-402: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2f&open=AZq84AEOvK_9d0Mfzo2f&pullRequest=189


[warning] 361-361: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2c&open=AZq84AEOvK_9d0Mfzo2c&pullRequest=189


[warning] 388-388: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2e&open=AZq84AEOvK_9d0Mfzo2e&pullRequest=189


[warning] 347-347: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2b&open=AZq84AEOvK_9d0Mfzo2b&pullRequest=189


[warning] 333-333: Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZq84AEOvK_9d0Mfzo2a&open=AZq84AEOvK_9d0Mfzo2a&pullRequest=189


[failure] 1043-1043: Remove usage of generic wildcard type.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZrBFOArZzgyS1d0Prb-&open=AZrBFOArZzgyS1d0Prb-&pullRequest=189

src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java

[warning] 24-24: Remove this unused import 'com.vaadin.flow.server.VaadinSession'.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_GridExporterAddon&issues=AZrBFODLZzgyS1d0Prb_&open=AZrBFODLZzgyS1d0Prb_&pullRequest=189

🔇 Additional comments (11)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java (1)

38-77: LGTM!

The class structure and delegation pattern are well-designed. The handleDownloadRequest method correctly wraps the delegate's handling within the semaphore-controlled runWithSemaphore method, ensuring consistent concurrency behavior with the existing ConcurrentStreamResourceWriter.

src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java (1)

38-89: LGTM!

The refactoring to extend ConcurrentOperationBase is well-executed. The class now properly delegates semaphore logic to the base class while maintaining its public API contract for setLimit/getLimit. The accept method correctly uses runWithSemaphore for concurrency control.

src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java (4)

52-81: LGTM!

The ConfigurableSemaphore implementation is well-designed. The setPermits method correctly handles both increasing and decreasing permits atomically through synchronization, and the toString method provides useful debugging output.


94-128: LGTM!

The setLimit and getLimit methods properly handle edge cases including infinite limits and provide thread-safe access to the semaphore configuration.


130-139: LGTM!

The costToPermits method correctly implements the fixed-point arithmetic for permit calculation. The behavior of returning 0 permits for zero or negative costs (allowing bypass of the semaphore) is consistent with the documented design. Based on learnings, this is the intended behavior.


220-257: LGTM!

The runWithSemaphore method has sound concurrency logic:

  • onAccept() is correctly called before any semaphore interaction (appropriate for UI state management)
  • Permits are properly released in the inner finally block only when acquired
  • InterruptedException correctly restores the thread's interrupt status
  • The outer finally ensures onFinish() is always called

The UI attachment check after semaphore acquisition provides a safeguard against processing requests for detached UIs when failOnUiChange is enabled.

src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java (5)

45-46: LGTM!

The new DownloadHandler import is correctly added to support the new API.


310-405: LGTM!

The deprecation of StreamResource-based methods is properly implemented with:

  • @Deprecated(since = "2.6.0", forRemoval = true) annotations
  • Javadoc pointing to the new DownloadHandler alternatives
  • Clear indication of removal timeline (version 3.0.0)

This provides a smooth migration path as stated in the PR objectives. The SonarCloud warnings are expected reminders for future cleanup.


406-489: LGTM!

The new DownloadHandler methods are well-implemented:

  • DOCX, PDF, and Excel exports correctly use makeConcurrentDownloadHandler for concurrency control
  • CSV export correctly uses StreamResourceWriterAdapter directly without concurrency wrapper, consistent with the existing behavior where CSV exports are not subject to concurrent controls (based on learnings)
  • MIME types are correctly specified for each format

496-500: LGTM!

The makeConcurrentDownloadHandler helper properly composes the writer into a concurrent-aware download handler by first adapting it to DownloadHandler and then wrapping with concurrency control.


226-232: LGTM!

Minor formatting adjustment with no semantic changes.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@MIGRATION_GUIDE.md`:
- Around line 100-110: Update the "After" example to match the actual demo by
removing the unnecessary .forComponent(...) call: use
exporter.getExcelDownloadHandler() when assigning the Anchor's href (referencing
GridExporter.createFor and getExcelDownloadHandler), and add a note that
.forComponent(...) is not required for the DownloadHandler API in this usage; if
the handler needs component-scoped binding in other contexts the
.forComponent(component) helper remains available—also set the Anchor's download
attribute on the element when you want the browser to treat it as a downloadable
link.
- Around line 125-136: Update the "Features Preserved" section to clarify that
"Concurrent download control" does not apply to CSV exports: explicitly mention
that getCsvStreamResource returns a plain StreamResource (not a
GridExporterStreamResource) and therefore CSV exports bypass the
concurrency/throttling mechanism used by other export formats; update the bullet
or add a parenthetical note so readers won't expect concurrent throttling for
CSV.

In `@src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java`:
- Around line 171-177: The icon-only Anchor elements (e.g., the excelLink
created in GridExporter.java and the other similar Anchors for CSV, PDF, Print
links) lack accessible names; add an explicit accessible name attribute when
each Anchor is created by setting element attribute "aria-label" (or "title" for
redundancy) to a meaningful label drawn from the corresponding exporter tooltip
text property (e.g., exporter.excelExportTooltipText,
exporter.csvExportTooltipText, exporter.pdfExportTooltipText,
exporter.printExportTooltipText) or a sensible default like "Download Excel" /
"Download CSV" / "Download PDF" / "Print" if the tooltip text is null/empty; do
this unconditionally (independently of applyExportTooltip/tooltip configurator)
so screen readers can discover the action for the Anchors referenced in the
methods that create excelLink, csvLink, pdfLink, printLink and their variants.
- Around line 1167-1181: The onFinish() logic should not unconditionally
re-enable the bound component; modify onAccept(), setButtonEnabled(...) and
onFinish() to record the prior enabled state when you disable the button (e.g.,
store a Boolean previousEnabledState in the listener/exporter instance) and only
restore that recorded state in onFinish(); specifically, in onAccept() if
getExporter().disableOnClick is true capture the current enabled state of
getButton() (if it instanceof HasEnabled) before calling
setButtonEnabled(false), and in onFinish() check the stored previousEnabledState
and call setButtonEnabled(previousEnabledState) only when that state is present,
then clear the stored value; keep using the existing UI.access wrapper inside
setButtonEnabled when applying the HasEnabled.setEnabled call.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5bd43818-1229-4e00-9f5a-47dafd68b4aa

📥 Commits

Reviewing files that changed from the base of the PR and between e608b7c and 744e944.

📒 Files selected for processing (8)
  • MIGRATION_GUIDE.md
  • README.md
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java
  • src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterCustomLinkDemo.java
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterCustomLinkDemo.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@MIGRATION_GUIDE.md`:
- Around line 100-110: Update the "After" example to match the actual demo by
removing the unnecessary .forComponent(...) call: use
exporter.getExcelDownloadHandler() when assigning the Anchor's href (referencing
GridExporter.createFor and getExcelDownloadHandler), and add a note that
.forComponent(...) is not required for the DownloadHandler API in this usage; if
the handler needs component-scoped binding in other contexts the
.forComponent(component) helper remains available—also set the Anchor's download
attribute on the element when you want the browser to treat it as a downloadable
link.
- Around line 125-136: Update the "Features Preserved" section to clarify that
"Concurrent download control" does not apply to CSV exports: explicitly mention
that getCsvStreamResource returns a plain StreamResource (not a
GridExporterStreamResource) and therefore CSV exports bypass the
concurrency/throttling mechanism used by other export formats; update the bullet
or add a parenthetical note so readers won't expect concurrent throttling for
CSV.

In `@src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java`:
- Around line 171-177: The icon-only Anchor elements (e.g., the excelLink
created in GridExporter.java and the other similar Anchors for CSV, PDF, Print
links) lack accessible names; add an explicit accessible name attribute when
each Anchor is created by setting element attribute "aria-label" (or "title" for
redundancy) to a meaningful label drawn from the corresponding exporter tooltip
text property (e.g., exporter.excelExportTooltipText,
exporter.csvExportTooltipText, exporter.pdfExportTooltipText,
exporter.printExportTooltipText) or a sensible default like "Download Excel" /
"Download CSV" / "Download PDF" / "Print" if the tooltip text is null/empty; do
this unconditionally (independently of applyExportTooltip/tooltip configurator)
so screen readers can discover the action for the Anchors referenced in the
methods that create excelLink, csvLink, pdfLink, printLink and their variants.
- Around line 1167-1181: The onFinish() logic should not unconditionally
re-enable the bound component; modify onAccept(), setButtonEnabled(...) and
onFinish() to record the prior enabled state when you disable the button (e.g.,
store a Boolean previousEnabledState in the listener/exporter instance) and only
restore that recorded state in onFinish(); specifically, in onAccept() if
getExporter().disableOnClick is true capture the current enabled state of
getButton() (if it instanceof HasEnabled) before calling
setButtonEnabled(false), and in onFinish() check the stored previousEnabledState
and call setButtonEnabled(previousEnabledState) only when that state is present,
then clear the stored value; keep using the existing UI.access wrapper inside
setButtonEnabled when applying the HasEnabled.setEnabled call.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5bd43818-1229-4e00-9f5a-47dafd68b4aa

📥 Commits

Reviewing files that changed from the base of the PR and between e608b7c and 744e944.

📒 Files selected for processing (8)
  • MIGRATION_GUIDE.md
  • README.md
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java
  • src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterCustomLinkDemo.java
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterCustomLinkDemo.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java
🛑 Comments failed to post (4)
MIGRATION_GUIDE.md (2)

100-110: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Inconsistent .forComponent() usage across examples.

The "After" example here shows .forComponent(customLink) being used (line 110), but the actual working demo code in GridExporterCustomLinkDemo.java line 77 does not use .forComponent() at all:

excelLink.setHref(exporter.getExcelDownloadHandler());

Additionally, the "Custom Implementation" example later in this same guide (lines 179-181) also omits .forComponent(). This creates conflicting guidance for users migrating their code.

Please clarify: Is .forComponent() needed with the new DownloadHandler API? If not, update this example to match the actual demo. If it is optional or context-dependent, add a note explaining when it should be used.

📝 Proposed fix to align with actual demo usage
 **After:**
 ```java
 GridExporter<Person> exporter = GridExporter.createFor(grid);
 exporter.setAutoAttachExportButtons(false);

 Anchor customLink = new Anchor("", "Download Excel");
-customLink.setHref(exporter.getExcelDownloadHandler().forComponent(customLink));
+customLink.setHref(exporter.getExcelDownloadHandler());
+customLink.getElement().setAttribute("download", true);

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @MIGRATION_GUIDE.md around lines 100 - 110, Update the "After" example to
match the actual demo by removing the unnecessary .forComponent(...) call: use
exporter.getExcelDownloadHandler() when assigning the Anchor's href (referencing
GridExporter.createFor and getExcelDownloadHandler), and add a note that
.forComponent(...) is not required for the DownloadHandler API in this usage; if
the handler needs component-scoped binding in other contexts the
.forComponent(component) helper remains available—also set the Anchor's download
attribute on the element when you want the browser to treat it as a downloadable
link.


</details>

<!-- fingerprinting:phantom:triton:puma -->

<!-- This is an auto-generated comment by CodeRabbit -->

---

125-136: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Clarify that concurrent control doesn't apply to CSV exports.**

Line 129 lists "Concurrent download control" as a preserved feature for all exports. However, based on learnings from previous reviews, CSV exports are not subject to concurrent controls—`getCsvStreamResource` returns a plain `StreamResource` rather than a `GridExporterStreamResource` specifically because CSV bypasses concurrency management.

This blanket statement could mislead users into expecting concurrent throttling for CSV exports when it doesn't apply.





<details>
<summary>📝 Proposed clarification</summary>

```diff
 All existing features continue to work with the new API:

-- ✅ Concurrent download control
+- ✅ Concurrent download control (Excel, DOCX, PDF)
 - ✅ Download timeouts
 - ✅ Button disable/enable during download
 - ✅ Custom templates
 - ✅ All export formats (Excel, DOCX, PDF, CSV)

Based on learnings from PR #143 indicating that CSV exports do not use concurrent controls.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

## Features Preserved

All existing features continue to work with the new API:

- ✅ Concurrent download control (Excel, DOCX, PDF)
- ✅ Download timeouts
- ✅ Button disable/enable during download
- ✅ Custom templates
- ✅ All export formats (Excel, DOCX, PDF, CSV)
- ✅ Custom columns and headers
- ✅ Hierarchical data support

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@MIGRATION_GUIDE.md` around lines 125 - 136, Update the "Features Preserved"
section to clarify that "Concurrent download control" does not apply to CSV
exports: explicitly mention that getCsvStreamResource returns a plain
StreamResource (not a GridExporterStreamResource) and therefore CSV exports
bypass the concurrency/throttling mechanism used by other export formats; update
the bullet or add a parenthetical note so readers won't expect concurrent
throttling for CSV.
src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java (2)

171-177: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Give the icon-only export links an accessible name.

These Anchors render only an icon, and Tooltip.forComponent(...).setText(...) does not give assistive tech a programmatic label. Screen readers will still hit unnamed links, so the export actions are not discoverable without a mouse. Set an aria-label/accessible name when each link is created, independently from whether the visual tooltip is enabled.

♿ Suggested fix
-  private static void applyExportTooltip(Component component, String text,
-      SerializableConsumer<Tooltip> configurator) {
+  private static void applyExportTooltip(Component component, String ariaLabel, String text,
+      SerializableConsumer<Tooltip> configurator) {
+    component.getElement().setAttribute("aria-label", ariaLabel);
     if (text == null) {
       return;
     }
     Tooltip tooltip = Tooltip.forComponent(component);
     tooltip.setText(text);
-                  applyExportTooltip(excelLink, exporter.excelExportTooltipText,
+                  applyExportTooltip(excelLink, "Export to Excel",
+                      exporter.excelExportTooltipText,
                       exporter.excelExportTooltipConfigurator);
...
-                  applyExportTooltip(docLink, exporter.docxExportTooltipText,
+                  applyExportTooltip(docLink, "Export to Word",
+                      exporter.docxExportTooltipText,
                       exporter.docxExportTooltipConfigurator);
...
-                  applyExportTooltip(docLink, exporter.pdfExportTooltipText,
+                  applyExportTooltip(docLink, "Export to PDF",
+                      exporter.pdfExportTooltipText,
                       exporter.pdfExportTooltipConfigurator);
...
-                  applyExportTooltip(csvLink, exporter.csvExportTooltipText,
+                  applyExportTooltip(csvLink, "Export to CSV",
+                      exporter.csvExportTooltipText,
                       exporter.csvExportTooltipConfigurator);

Also applies to: 182-187, 192-206, 238-248

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java`
around lines 171 - 177, The icon-only Anchor elements (e.g., the excelLink
created in GridExporter.java and the other similar Anchors for CSV, PDF, Print
links) lack accessible names; add an explicit accessible name attribute when
each Anchor is created by setting element attribute "aria-label" (or "title" for
redundancy) to a meaningful label drawn from the corresponding exporter tooltip
text property (e.g., exporter.excelExportTooltipText,
exporter.csvExportTooltipText, exporter.pdfExportTooltipText,
exporter.printExportTooltipText) or a sensible default like "Download Excel" /
"Download CSV" / "Download PDF" / "Print" if the tooltip text is null/empty; do
this unconditionally (independently of applyExportTooltip/tooltip configurator)
so screen readers can discover the action for the Anchors referenced in the
methods that create excelLink, csvLink, pdfLink, printLink and their variants.

1167-1181: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Don't always re-enable the bound component on finish.

onFinish() unconditionally calls setButtonEnabled(true). If the component was already disabled for some other reason, or disableOnClick is false, a completed or timed-out export will still flip it back on. Track whether this strategy actually disabled the component and only restore in that case, or capture and restore the previous enabled state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java`
around lines 1167 - 1181, The onFinish() logic should not unconditionally
re-enable the bound component; modify onAccept(), setButtonEnabled(...) and
onFinish() to record the prior enabled state when you disable the button (e.g.,
store a Boolean previousEnabledState in the listener/exporter instance) and only
restore that recorded state in onFinish(); specifically, in onAccept() if
getExporter().disableOnClick is true capture the current enabled state of
getButton() (if it instanceof HasEnabled) before calling
setButtonEnabled(false), and in onFinish() check the stored previousEnabledState
and call setButtonEnabled(previousEnabledState) only when that state is present,
then clear the stored value; keep using the existing UI.access wrapper inside
setButtonEnabled when applying the HasEnabled.setEnabled call.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java (1)

50-60: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Javadoc references incorrect type for delegate parameter.

Line 56 references InputStreamFactory but the parameter type is StreamResourceWriter.

📝 Proposed fix
-   * `@param` delegate the delegate {`@code` InputStreamFactory}
+   * `@param` delegate the delegate {`@code` StreamResourceWriter}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java`
around lines 50 - 60, Update the Javadoc for the
ConcurrentStreamResourceWriter(StreamResourceWriter delegate) constructor to
reference the correct type: replace the incorrect {`@code` InputStreamFactory}
mention with {`@link` StreamResourceWriter} (or plain StreamResourceWriter) in the
`@param` description and any explanatory text so the doc matches the constructor
parameter named delegate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@MIGRATION_GUIDE.md`:
- Around line 105-111: The migration example is inconsistent about how to attach
the Excel download handler; update the snippet so it matches the demo/API usage
by using the same pattern as the demo (use
Anchor.setHref(exporter.getExcelDownloadHandler()) if the handler returns a
String/URL, or consistently call
exporter.getExcelDownloadHandler().forComponent(customLink) if the handler
requires the component context); modify the MIGRATION_GUIDE example to use the
canonical pattern chosen and add a short parenthetical note when to use
forComponent versus passing the handler directly; reference
GridExporter.createFor, setAutoAttachExportButtons, Anchor, and
getExcelDownloadHandler() to locate and update the code sample and explanation.
- Line 5: Update the phrasing on the Vaadin 25 compatibility note so it says
that StreamResource is "deprecated and marked for removal" (or "deprecated and
may be removed in a future Vaadin release") instead of "will be removed", while
keeping the add-on timeline noting migration to DownloadHandler-based APIs
(starting in version 2.6.0) and the add-on removal target (version 3.0.0)
intact; then reconcile the custom-link example to match the demo by either
changing the guide's example from
customLink.setHref(exporter.getExcelDownloadHandler().forComponent(customLink))
to excelLink.setHref(exporter.getExcelDownloadHandler()) as shown in
GridExporterCustomLinkDemo, or add a one-line clarification explaining when
.forComponent(...) is required (e.g., when the DownloadHandler needs
component-specific context) and why the demo omits it.

---

Outside diff comments:
In
`@src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java`:
- Around line 50-60: Update the Javadoc for the
ConcurrentStreamResourceWriter(StreamResourceWriter delegate) constructor to
reference the correct type: replace the incorrect {`@code` InputStreamFactory}
mention with {`@link` StreamResourceWriter} (or plain StreamResourceWriter) in the
`@param` description and any explanatory text so the doc matches the constructor
parameter named delegate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 75506d3b-9736-47dd-a82f-436320dbd961

📥 Commits

Reviewing files that changed from the base of the PR and between 744e944 and dd0d365.

📒 Files selected for processing (8)
  • MIGRATION_GUIDE.md
  • README.md
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java
  • src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterCustomLinkDemo.java
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterCustomLinkDemo.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java

Comment thread MIGRATION_GUIDE.md Outdated
Comment thread MIGRATION_GUIDE.md
mlopezFC added 2 commits June 3, 2026 20:31
Extract common semaphore logic into ConcurrentOperationBase for reuse in concurrent handlers.
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 3, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
8.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@paodb
Copy link
Copy Markdown
Member

paodb commented Jun 4, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 4, 2026

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java (1)

1179-1179: ⚡ Quick win

Consider using pattern matching instanceof (Java 16+).

The code can be simplified using pattern matching:

-      if (button instanceof HasEnabled) {
-        getExporter().grid.getUI().ifPresent(ui -> ui.access(() -> ((HasEnabled) button).setEnabled(enabled)));
+      if (button instanceof HasEnabled hasEnabled) {
+        getExporter().grid.getUI().ifPresent(ui -> ui.access(() -> hasEnabled.setEnabled(enabled)));
       }

This is a modern Java idiom that reduces casting and improves readability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java`
at line 1179, Replace the classic instanceof + cast pattern around the local
variable "button" with Java's pattern matching instanceof to simplify and remove
the explicit cast; specifically update the check using "button instanceof
HasEnabled" so it declares the matched variable (e.g., "button instanceof
HasEnabled hasEnabled") and then use that matched variable instead of
casting—apply this change in GridExporter.java wherever the current "if (button
instanceof HasEnabled) { ... ((HasEnabled) button) ..." pattern appears.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java`:
- Line 1179: Replace the classic instanceof + cast pattern around the local
variable "button" with Java's pattern matching instanceof to simplify and remove
the explicit cast; specifically update the check using "button instanceof
HasEnabled" so it declares the matched variable (e.g., "button instanceof
HasEnabled hasEnabled") and then use that matched variable instead of
casting—apply this change in GridExporter.java wherever the current "if (button
instanceof HasEnabled) { ... ((HasEnabled) button) ..." pattern appears.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7c420953-5006-4fb8-963a-127d25a5fc69

📥 Commits

Reviewing files that changed from the base of the PR and between 744e944 and 7dc08a4.

📒 Files selected for processing (8)
  • MIGRATION_GUIDE.md
  • README.md
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentStreamResourceWriter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridExporter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java
  • src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterCustomLinkDemo.java
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterCustomLinkDemo.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/StreamResourceWriterAdapter.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentDownloadHandler.java
  • src/main/java/com/flowingcode/vaadin/addons/gridexporter/ConcurrentOperationBase.java

@paodb paodb marked this pull request as ready for review June 4, 2026 12:43
@paodb paodb requested review from javier-godoy and scardanzan June 4, 2026 12:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To Do

Development

Successfully merging this pull request may close these issues.

StreamResource is deprecated in favor of DownloadHandler

2 participants