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

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new d07c767358 cleanup and simplify hop web for tab rendering in Linux and 
Hop Web. … (#6247)
d07c767358 is described below

commit d07c767358b4382e320632276acae7e974e9017f
Author: Bart Maertens <[email protected]>
AuthorDate: Tue Dec 23 13:34:53 2025 +0000

    cleanup and simplify hop web for tab rendering in Linux and Hop Web. … 
(#6247)
    
    * cleanup and simplify hop web for tab rendering in Linux and Hop Web. 
fixes #6246
    
    * spotless update. #6246
---
 docker/unified.Dockerfile                          |  14 ---
 .../apache/hop/ui/core/SafeCTabFolderRenderer.java | 117 +++++++++++++++++++++
 .../main/java/org/apache/hop/ui/core/PropsUi.java  |  29 +++--
 .../apache/hop/ui/core/SafeCTabFolderRenderer.java |  85 ---------------
 4 files changed, 138 insertions(+), 107 deletions(-)

diff --git a/docker/unified.Dockerfile b/docker/unified.Dockerfile
index 64c48df365..d1bf099fac 100644
--- a/docker/unified.Dockerfile
+++ b/docker/unified.Dockerfile
@@ -189,20 +189,6 @@ RUN mkdir -p /build/hop-web-prepared/webapps/ROOT && \
     cp -r /build/assemblies/client/target/hop/lib/beam/* 
/build/hop-web-prepared/webapps/ROOT/WEB-INF/lib/ && \
     cp -r /build/assemblies/client/target/hop/lib/core/* 
/build/hop-web-prepared/webapps/ROOT/WEB-INF/lib/ && \
     rm /build/hop-web-prepared/webapps/ROOT/WEB-INF/lib/hop-ui-rcp* && \
-    # Remove SafeCTabFolderRenderer.class from hop-ui JAR - it extends 
CTabFolderRenderer
-    # which doesn't exist in RAP/web mode, causing NoClassDefFoundError
-    for jar in /build/hop-web-prepared/webapps/ROOT/WEB-INF/lib/hop-ui-*.jar; 
do \
-        if [ -f "$jar" ]; then \
-            cd /tmp && \
-            unzip -q "$jar" -d hop-ui-temp && \
-            rm -f 
hop-ui-temp/org/apache/hop/ui/core/SafeCTabFolderRenderer.class && \
-            rm -f "$jar" && \
-            cd hop-ui-temp && \
-            zip -q -r "$jar" . && \
-            cd /build && \
-            rm -rf /tmp/hop-ui-temp; \
-        fi; \
-    done && \
     mkdir -p /build/hop-web-prepared/bin && \
     cp -r /build/docker/resources/run-web.sh 
/build/hop-web-prepared/bin/run-web.sh
 
diff --git 
a/rcp/src/main/java/org/apache/hop/ui/core/SafeCTabFolderRenderer.java 
b/rcp/src/main/java/org/apache/hop/ui/core/SafeCTabFolderRenderer.java
new file mode 100644
index 0000000000..d3ea3a50c9
--- /dev/null
+++ b/rcp/src/main/java/org/apache/hop/ui/core/SafeCTabFolderRenderer.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.core;
+
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabFolderRenderer;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+
+/**
+ * A safe CTabFolderRenderer that catches IllegalArgumentException when 
drawing tab images. This
+ * prevents crashes on some Linux desktop environments (e.g., KDE 
Plasma/Wayland) where images may
+ * be invalid or disposed during rendering. When an image drawing error 
occurs, the image is skipped
+ * but the text is still rendered.
+ *
+ * <p>This class is in the RCP module (desktop-specific) and loaded via 
reflection from
+ * PropsUi.ensureSafeRenderer(). Since that method short-circuits in web mode 
(isWeb() check), and
+ * this class is not included in web builds (hop-ui-rcp is excluded), it will 
never be loaded in
+ * RAP/web mode.
+ */
+public class SafeCTabFolderRenderer extends CTabFolderRenderer {
+  private final CTabFolder parentFolder;
+
+  public SafeCTabFolderRenderer(CTabFolder parent) {
+    super(parent);
+    this.parentFolder = parent;
+  }
+
+  @Override
+  protected void draw(int part, int state, Rectangle bounds, GC gc) {
+    if (bounds != null && (bounds.width <= 0 || bounds.height <= 0)) {
+      return;
+    }
+
+    // Check for invalid/disposed images before drawing to prevent 
IllegalArgumentException
+    // on Linux environments (e.g., KDE Plasma/Wayland) where images may be 
invalid or disposed
+    Image[] savedImages = null;
+    boolean hadInvalidImages = false;
+
+    if (parentFolder != null && !parentFolder.isDisposed()) {
+      CTabItem[] items = parentFolder.getItems();
+      savedImages = new Image[items.length];
+
+      // Check each tab item's image for validity before drawing
+      for (int i = 0; i < items.length; i++) {
+        Image image = items[i].getImage();
+        savedImages[i] = image;
+
+        // If image is disposed/invalid, temporarily remove it to prevent 
rendering errors
+        if (image != null && image.isDisposed()) {
+          items[i].setImage(null);
+          hadInvalidImages = true;
+        }
+      }
+    }
+
+    try {
+      super.draw(part, state, bounds, gc);
+    } catch (IllegalArgumentException e) {
+      // If image drawing still fails (e.g., image becomes invalid during 
drawing),
+      // temporarily remove all images and redraw to allow text to be rendered
+      if (parentFolder != null && !parentFolder.isDisposed()) {
+        CTabItem[] items = parentFolder.getItems();
+
+        // Clear all images that weren't already cleared
+        for (int i = 0; i < items.length && savedImages != null && i < 
savedImages.length; i++) {
+          if (savedImages[i] != null && items[i].getImage() != null) {
+            items[i].setImage(null);
+          }
+        }
+
+        try {
+          // Redraw without images - this should succeed and render the text
+          super.draw(part, state, bounds, gc);
+        } finally {
+          // Restore images (even if they're invalid, we'll catch it next time)
+          restoreImages(items, savedImages);
+        }
+      }
+    } finally {
+      // Restore images that were temporarily removed due to being disposed
+      if (hadInvalidImages && parentFolder != null && 
!parentFolder.isDisposed()) {
+        restoreImages(parentFolder.getItems(), savedImages);
+      }
+    }
+  }
+
+  /** Restores images to tab items, skipping any that are disposed */
+  private void restoreImages(CTabItem[] items, Image[] savedImages) {
+    if (items == null || savedImages == null) {
+      return;
+    }
+
+    for (int i = 0; i < items.length && i < savedImages.length; i++) {
+      if (savedImages[i] != null && !savedImages[i].isDisposed()) {
+        items[i].setImage(savedImages[i]);
+      }
+    }
+  }
+}
diff --git a/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java 
b/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
index 9fa1db70ac..03a9869072 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
@@ -522,30 +522,43 @@ public class PropsUi extends Props {
    * on Wayland) where images may be invalid or disposed. Only applied on 
Linux to avoid any
    * potential impact on unaffected platforms.
    *
+   * <p>SafeCTabFolderRenderer is in the RCP module (desktop-specific) and 
loaded via reflection.
+   * Since this method short-circuits in web mode and SafeCTabFolderRenderer 
is not included in web
+   * builds (hop-ui-rcp is excluded), it will never be loaded in RAP/web mode.
+   *
    * @param tabFolder the CTabFolder to protect
    */
   private static void ensureSafeRenderer(CTabFolder tabFolder) {
-    // CTabFolderRenderer and SafeCTabFolderRenderer are not available in RAP 
(web mode),
-    // so skip entirely. The class is also removed from the hop-ui JAR during 
Docker build.
-    // Use reflection to avoid compile-time class resolution that would cause 
NoClassDefFoundError
-    // in web mode when the class doesn't exist in the JAR.
+    // CTabFolderRenderer is not available in RAP (web mode), so skip entirely.
+    // SafeCTabFolderRenderer is in the RCP module (not included in web 
builds), so it's never
+    // available in web mode.
     if (EnvironmentUtils.getInstance().isWeb()) {
       return;
     }
     // Only apply safe renderer on Linux where the issue occurs
     if (Const.isLinux()) {
       try {
-        // Use reflection to avoid compile-time dependency on 
SafeCTabFolderRenderer
-        // This prevents Java's bytecode verifier from trying to resolve the 
class at load time
+        // Use reflection to load SafeCTabFolderRenderer from the RCP module 
(desktop-specific).
+        // This avoids compile-time dependencies and ensures the class isn't 
loaded in web mode
+        // (where hop-ui-rcp is excluded from the build).
         Class<?> rendererClass = 
Class.forName("org.apache.hop.ui.core.SafeCTabFolderRenderer");
         Object currentRenderer = tabFolder.getRenderer();
         if (currentRenderer == null || 
!rendererClass.isInstance(currentRenderer)) {
           Object safeRenderer =
               
rendererClass.getConstructor(CTabFolder.class).newInstance(tabFolder);
-          tabFolder.setRenderer((org.eclipse.swt.custom.CTabFolderRenderer) 
safeRenderer);
+          // Use reflection to call setRenderer to avoid compile-time 
dependency on
+          // CTabFolderRenderer which doesn't exist in RAP/web mode
+          Class<?> rendererParamClass = 
Class.forName("org.eclipse.swt.custom.CTabFolderRenderer");
+          java.lang.reflect.Method setRendererMethod =
+              CTabFolder.class.getMethod("setRenderer", rendererParamClass);
+          setRendererMethod.invoke(tabFolder, safeRenderer);
         }
+      } catch (ClassNotFoundException e) {
+        // SafeCTabFolderRenderer not available (e.g., in web builds where 
hop-ui-rcp is excluded)
+        // This is expected and safe to ignore
       } catch (Exception e) {
-        // If SafeCTabFolderRenderer can't be loaded, just continue without 
the safe renderer
+        // If CTabFolderRenderer can't be loaded or setRenderer fails, just 
continue without
+        // the safe renderer
         LogChannel.GENERAL.logDetailed("Could not apply 
SafeCTabFolderRenderer: " + e.getMessage());
       }
     }
diff --git 
a/ui/src/main/java/org/apache/hop/ui/core/SafeCTabFolderRenderer.java 
b/ui/src/main/java/org/apache/hop/ui/core/SafeCTabFolderRenderer.java
deleted file mode 100644
index 7d5a3830cc..0000000000
--- a/ui/src/main/java/org/apache/hop/ui/core/SafeCTabFolderRenderer.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hop.ui.core;
-
-import org.eclipse.swt.custom.CTabFolder;
-import org.eclipse.swt.custom.CTabFolderRenderer;
-import org.eclipse.swt.custom.CTabItem;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Rectangle;
-
-/**
- * A safe CTabFolderRenderer that catches IllegalArgumentException when 
drawing tab images. This
- * prevents crashes on some Linux desktop environments where images may be 
invalid or disposed
- * during rendering. When an image drawing error occurs, the image is skipped 
but the text is still
- * rendered.
- *
- * <p>This class is in a separate file to avoid loading CTabFolderRenderer 
(which doesn't exist in
- * RAP/web mode) when PropsUi is loaded. The class is only instantiated on 
Linux desktop
- * environments where it's needed.
- */
-public class SafeCTabFolderRenderer extends CTabFolderRenderer {
-  private final CTabFolder parentFolder;
-
-  public SafeCTabFolderRenderer(CTabFolder parent) {
-    super(parent);
-    this.parentFolder = parent;
-  }
-
-  @Override
-  protected void draw(int part, int state, Rectangle bounds, GC gc) {
-    if (bounds != null && (bounds.width <= 0 || bounds.height <= 0)) {
-      return;
-    }
-    try {
-      super.draw(part, state, bounds, gc);
-    } catch (IllegalArgumentException e) {
-      // If image drawing fails, temporarily remove images from tab items and 
redraw
-      // This allows text to be rendered even when images are invalid
-      if (parentFolder != null && !parentFolder.isDisposed()) {
-        CTabItem[] items = parentFolder.getItems();
-        Image[] savedImages = new Image[items.length];
-        boolean hadImages = false;
-
-        // Save and temporarily clear images
-        for (int i = 0; i < items.length; i++) {
-          savedImages[i] = items[i].getImage();
-          if (savedImages[i] != null) {
-            items[i].setImage(null);
-            hadImages = true;
-          }
-        }
-
-        if (hadImages) {
-          try {
-            // Redraw without images - this should succeed and render the text
-            super.draw(part, state, bounds, gc);
-          } finally {
-            // Restore images (even if they're invalid, we'll catch the 
exception next time)
-            for (int i = 0; i < items.length; i++) {
-              if (savedImages[i] != null) {
-                items[i].setImage(savedImages[i]);
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-}

Reply via email to