This is an automated email from the ASF dual-hosted git repository. ardovm pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 45c5a00162811bd03b87fcb4fe9996782f2b59ec Author: Piotr P. Karwasz <[email protected]> AuthorDate: Fri Jul 3 09:16:41 2026 +0200 Skip invalid entries Detect malformed URLs, log them and and skip them. The per-entry handling is factored into a shared Tools.addClassPathURL helper used by both ClassMap variants, with ToolsTest covering the accepted and skipped schemes (subsequent JUnit test under connectivity/qa/complex/sdbc). Assisted-By: Claude Opus 4.8 <[email protected]> --- .../main/java/com/sun/star/comp/sdbc/ClassMap.java | 9 +- .../main/java/com/sun/star/comp/sdbc/Tools.java | 67 +++++++++++ .../sun/star/comp/sdbc/classloading/ClassMap.java | 11 +- main/connectivity/qa/complex/sdbc/ToolsTest.java | 134 +++++++++++++++++++++ main/connectivity/qa/complex/sdbc/makefile.mk | 58 +++++++++ 5 files changed, 269 insertions(+), 10 deletions(-) diff --git a/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/ClassMap.java b/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/ClassMap.java index 6bdd506af2..fc7b93d698 100644 --- a/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/ClassMap.java +++ b/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/ClassMap.java @@ -21,7 +21,6 @@ package com.sun.star.comp.sdbc; import java.lang.ref.WeakReference; -import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; @@ -86,7 +85,7 @@ public class ClassMap { private final LinkedList<ClassMapEntry> map = new LinkedList<>(); public synchronized ClassLoaderAndClass loadClass(XComponentContext context, String classPath, String className) - throws MalformedURLException, ClassNotFoundException { + throws ClassNotFoundException { ClassLoader classLoader = null; Class<?> classObject = null; @@ -130,7 +129,7 @@ public class ClassMap { return new ClassLoaderAndClass(classLoader, classObject); } - private static List<URL> translateToUrls(XComponentContext context, String classPath) throws MalformedURLException { + private static List<URL> translateToUrls(XComponentContext context, String classPath) { StringTokenizer tokenizer = new StringTokenizer(classPath, " ", false); ArrayList<URL> urls = new ArrayList<>(); while (tokenizer.hasMoreTokens()) { @@ -159,8 +158,8 @@ public class ClassMap { CompHelper.disposeComponent(expUrl); CompHelper.disposeComponent(macroExpander); } - URL javaURL = new URL(url); - urls.add(javaURL); + // Add local entries to classpath + Tools.addClassPathURL(urls, url); } return urls; } diff --git a/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/Tools.java b/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/Tools.java index 49cb07cb0f..9725949b32 100644 --- a/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/Tools.java +++ b/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/Tools.java @@ -20,6 +20,16 @@ *************************************************************/ package com.sun.star.comp.sdbc; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + import org.apache.openoffice.comp.sdbc.dbtools.comphelper.ResourceBasedEventLogger; import org.apache.openoffice.comp.sdbc.dbtools.util.StandardSQLState; @@ -34,6 +44,17 @@ import com.sun.star.uno.AnyConverter; public class Tools { private static final int MAX_EXCEPTION_NESTING = 8; + private static final Logger LOGGER = Logger.getLogger(Tools.class.getName()); + + /** + * URL schemes that resolve to the local filesystem or the running JVM image. + * + * <p>jvmaccess/source/classpath.cxx enforces the same allow-list in C++ for + * the UNO bootstrap class path; keep the two in sync.</p> + */ + private static final Set<String> LOCAL_PROTOCOLS = + Collections.unmodifiableSet(new HashSet<>(Arrays.asList("file", "jrt", "jmod"))); + public static SQLException toUnoException(Object source, Throwable throwable) { return toUnoException(source, throwable, 0); } @@ -128,4 +149,50 @@ public class Tools { } return ret; } + + /** + * Appends a class path entry to the list of URLs used to build a class loader. + * + * <p>Only local entries or a jar: wrapping a local entry are added. + * A malformed or non-local entry is logged and skipped.</p> + * + * @param urls the list of class path URLs to append to + * @param url the class path entry to parse and validate + */ + public static void addClassPathURL(Collection<URL> urls, String url) { + URL javaURL; + String protocol; + try { + javaURL = new URL(url); + protocol = getEffectiveProtocol(javaURL); + } catch (MalformedURLException e) { + LOGGER.log(Level.WARNING, e, () -> "Skipping malformed class path entry: " + url); + return; + } + if (LOCAL_PROTOCOLS.contains(protocol)) { + LOGGER.fine(() -> "Adding class path entry: " + url); + urls.add(javaURL); + } else { + LOGGER.warning(() -> "Skipping non-local class path entry: " + url); + } + } + + /** + * Returns the scheme that actually locates the resource. + * + * <p>Since {@code jar:} only wraps another URL, the scheme of that wrapped URL is returned. + * For any other URL its own scheme is returned.</p> + * + * @param url the class path URL to inspect + * @return the effective URL scheme + * @throws MalformedURLException if the wrapped jar: URL cannot be parsed + */ + private static String getEffectiveProtocol(URL url) throws MalformedURLException { + if (!"jar".equals(url.getProtocol())) { + return url.getProtocol(); + } + String path = url.getPath(); + int separator = path.lastIndexOf("!/"); + return new URL(separator == -1 ? path : path.substring(0, separator)).getProtocol(); + } } diff --git a/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/classloading/ClassMap.java b/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/classloading/ClassMap.java index b8a03af528..13bf139a93 100644 --- a/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/classloading/ClassMap.java +++ b/main/connectivity/java/sdbc_jdbc/src/main/java/com/sun/star/comp/sdbc/classloading/ClassMap.java @@ -21,7 +21,6 @@ package com.sun.star.comp.sdbc.classloading; import java.lang.ref.WeakReference; -import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; @@ -32,6 +31,8 @@ import java.util.StringTokenizer; import org.apache.openoffice.comp.sdbc.dbtools.comphelper.CompHelper; +import com.sun.star.comp.sdbc.Tools; + import com.sun.star.lang.IllegalArgumentException; import com.sun.star.uno.RuntimeException; import com.sun.star.uno.UnoRuntime; @@ -69,7 +70,7 @@ public class ClassMap { private final LinkedList<ClassMapEntry> map = new LinkedList<>(); public synchronized ClassLoaderAndClass loadClass(XComponentContext context, String classPath, String className) - throws MalformedURLException, ClassNotFoundException { + throws ClassNotFoundException { ClassLoader classLoader = null; Class<?> classObject = null; @@ -113,7 +114,7 @@ public class ClassMap { return new ClassLoaderAndClass(classLoader, classObject); } - private static List<URL> translateToUrls(XComponentContext context, String classPath) throws MalformedURLException { + private static List<URL> translateToUrls(XComponentContext context, String classPath) { StringTokenizer tokenizer = new StringTokenizer(classPath, " ", false); ArrayList<URL> urls = new ArrayList<>(); while (tokenizer.hasMoreTokens()) { @@ -142,8 +143,8 @@ public class ClassMap { CompHelper.disposeComponent(expUrl); CompHelper.disposeComponent(macroExpander); } - URL javaURL = new URL(url); - urls.add(javaURL); + // Add local entries to classpath + Tools.addClassPathURL(urls, url); } return urls; } diff --git a/main/connectivity/qa/complex/sdbc/ToolsTest.java b/main/connectivity/qa/complex/sdbc/ToolsTest.java new file mode 100644 index 0000000000..3e5d1a9f85 --- /dev/null +++ b/main/connectivity/qa/complex/sdbc/ToolsTest.java @@ -0,0 +1,134 @@ +/************************************************************** + * + * 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 complex.sdbc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assume; +import org.junit.Test; + +import com.sun.star.comp.sdbc.Tools; + +/** + * Tests for the {@link Tools} helper class. + */ +public final class ToolsTest { + + /** Runs each entry through addClassPathURL and returns what was accepted. */ + private static List<URL> collect(String... entries) { + List<URL> urls = new ArrayList<URL>(); + for (String entry : entries) { + Tools.addClassPathURL(urls, entry); + } + return urls; + } + + /** True if the running JRE has a URL stream handler for the given scheme. */ + private static boolean schemeSupported(String scheme) { + try { + new URL(scheme + ":/probe"); + return true; + } catch (MalformedURLException e) { + return false; + } + } + + @Test + public void testAddClassPathURLAddsLocalFileEntry() { + List<URL> urls = collect("file:/opt/a.jar"); + assertEquals(1, urls.size()); + assertEquals("file", urls.get(0).getProtocol()); + } + + @Test + public void testAddClassPathURLAddsJarWrappedLocalFile() { + assertEquals(1, collect("jar:file:/opt/a.jar!/").size()); + } + + @Test + public void testAddClassPathURLTreatsJarInnerSchemeCaseInsensitively() { + // URL.getPath() does not normalize the wrapped URL. + assertEquals(1, collect("jar:FILE:/opt/a.jar!/").size()); + } + + @Test + public void testAddClassPathURLSkipsRemoteEntries() { + assertTrue(collect( + "http://host/a.jar", + "https://host/b.jar", + "ftp://host/c.jar").isEmpty()); + } + + @Test + public void testAddClassPathURLSkipsJarWrappedRemoteEntry() { + assertTrue(collect("jar:http://host/a.jar!/").isEmpty()); + } + + @Test + public void testAddClassPathURLSkipsMalformedEntry() { + assertTrue(collect("::not-a-url::").isEmpty()); + } + + @Test + public void testAddClassPathURLSkipsUnknownScheme() { + // A scheme with no registered URL handler cannot be constructed, so the + // entry is treated as malformed and skipped. + assertTrue(collect( + "wibble:/opt/a.jar", + "classpath:/opt/b.jar", + "foo://host/c.jar").isEmpty()); + } + + @Test + public void testAddClassPathURLSkipsJarWrappedUnknownScheme() { + assertTrue(collect("jar:wibble:/opt/a.jar!/").isEmpty()); + } + + @Test + public void testAddClassPathURLKeepsOnlyLocalEntriesInOrder() { + List<URL> urls = collect( + "http://host/a.jar", + "file:/opt/b.jar", + "jar:http://host/c.jar!/", + "file:/opt/d.jar"); + assertEquals(2, urls.size()); + assertEquals("/opt/b.jar", urls.get(0).getPath()); + assertEquals("/opt/d.jar", urls.get(1).getPath()); + } + + @Test + public void testAddClassPathURLAddsJrtSchemeWhenSupported() { + Assume.assumeTrue(schemeSupported("jrt")); + assertEquals(1, collect("jrt:/java.base/module-info.class").size()); + } + + @Test + public void testAddClassPathURLAddsJmodSchemeWhenSupported() { + Assume.assumeTrue(schemeSupported("jmod")); + assertEquals(1, collect("jmod:/x").size()); + } +} diff --git a/main/connectivity/qa/complex/sdbc/makefile.mk b/main/connectivity/qa/complex/sdbc/makefile.mk new file mode 100644 index 0000000000..64c1d0cffa --- /dev/null +++ b/main/connectivity/qa/complex/sdbc/makefile.mk @@ -0,0 +1,58 @@ +#************************************************************** +# +# 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. +# +#************************************************************** + + +.IF "$(OOO_SUBSEQUENT_TESTS)" == "" +nothing .PHONY: +.ELSE + +PRJ = ..$/..$/.. +PRJNAME = connectivity +TARGET = SdbcToolsTest + +.IF "$(OOO_JUNIT_JAR)" != "" +PACKAGE = complex$/sdbc + +# here store only Files which contain a @Test +JAVATESTFILES = \ + ToolsTest.java + +# put here all other files +JAVAFILES = $(JAVATESTFILES) + +# The class under test lives in sdbc_jdbc.jar; dbtools.jar supplies the two +# helper types Tools references (ResourceBasedEventLogger, StandardSQLState), +# so that com.sun.star.comp.sdbc.Tools can be loaded and verified. +JARFILES = ridl.jar unoil.jar jurt.jar juh.jar java_uno.jar dbtools.jar sdbc_jdbc.jar +EXTRAJARFILES = $(OOO_JUNIT_JAR) + +# Sample how to debug +# JAVAIFLAGS+=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=9003,suspend=y + +.END + +.INCLUDE: settings.mk +.INCLUDE: target.mk +.INCLUDE: installationtest.mk + +ALLTAR : javatest + +.END
