From 96b35cc49d126a2e74cd81f7523606027f72bed8 Mon Sep 17 00:00:00 2001 From: sebthom Date: Sat, 8 Nov 2025 22:17:30 +0100 Subject: [PATCH] fix(xml-catalog): preserve http/https URIs in system catalog WWD discarded XML catalog entries that were not file or jar URIs, omitting remote schemas contributed via WTP and triggering startup warnings. Accept http/https in XMLCatalogs.isSchemeSupportedInCatalog so LemMinX can resolve these remote schemas. --- .../wildwebdeveloper/tests/TestXML.java | 603 ++++++++++-------- .../internal/ui/preferences/XMLCatalogs.java | 9 +- 2 files changed, 349 insertions(+), 263 deletions(-) diff --git a/org.eclipse.wildwebdeveloper.tests/src/org/eclipse/wildwebdeveloper/tests/TestXML.java b/org.eclipse.wildwebdeveloper.tests/src/org/eclipse/wildwebdeveloper/tests/TestXML.java index 19b72d5c80..3b51838619 100644 --- a/org.eclipse.wildwebdeveloper.tests/src/org/eclipse/wildwebdeveloper/tests/TestXML.java +++ b/org.eclipse.wildwebdeveloper.tests/src/org/eclipse/wildwebdeveloper/tests/TestXML.java @@ -8,7 +8,7 @@ * SPDX-License-Identifier: EPL-2.0 * * Contributors: - * Mickael Istria (Red Hat Inc.) - initial implementation + * Mickael Istria (Red Hat Inc.) - initial implementation *******************************************************************************/ package org.eclipse.wildwebdeveloper.tests; @@ -60,262 +60,347 @@ @ExtendWith(AllCleanRule.class) public class TestXML { - private IProject project; - private ICompletionProposal[] proposals; - - @BeforeEach - public void setUpProject() throws CoreException { - this.project = ResourcesPlugin.getWorkspace().getRoot().getProject(getClass().getName() + System.nanoTime()); - project.create(null); - project.open(null); - } - - @Test - public void testXMLFile() throws Exception { - final IFile file = project.getFile("blah.xml"); - file.create("FAIL".getBytes(), true, false, null); - ITextEditor editor = (ITextEditor) IDE - .openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file); - editor.getDocumentProvider().getDocument(editor.getEditorInput()).set(" { - try { - return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0; - } catch (CoreException e) { - return false; - } - }), "Diagnostic not published"); - } - - @Test - public void testXSLFile() throws Exception { - final IFile file = project.getFile("blah.xsl"); - file.create("FAIL".getBytes(), true, false, null); - ITextEditor editor = (ITextEditor) IDE - .openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file); - editor.getDocumentProvider().getDocument(editor.getEditorInput()).set("FAIL"); - assertTrue(DisplayHelper.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000, () -> { - try { - return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0; - } catch (CoreException e) { - return false; - } - }), "Diagnostic not published"); - } - - @Test - public void testXSDFile() throws Exception { - final IFile file = project.getFile("blah.xsd"); - file.create("FAIL".getBytes(), true, false, null); - ITextEditor editor = (ITextEditor) IDE - .openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file); - editor.getDocumentProvider().getDocument(editor.getEditorInput()).set("a<"); - assertTrue(DisplayHelper.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000, () -> { - try { - return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0; - } catch (CoreException e) { - return false; - } - }), "Diagnostic not published"); - } - - @Test - public void testDTDFile() throws Exception { - final IFile file = project.getFile("blah.dtd"); - file.create("FAIL".getBytes(), true, false, null); - ITextEditor editor = (ITextEditor) IDE - .openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file); - editor.getDocumentProvider().getDocument(editor.getEditorInput()).set(""); - assertTrue(DisplayHelper.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000, () -> { - try { - return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0; - } catch (CoreException e) { - return false; - } - }), "Diagnostic not published"); - } - - @Test - public void testComplexXML() throws Exception { - final IFile file = project.getFile("blah.xml"); - String content = "\n" + " |\n" + ""; - int offset = content.indexOf('|'); - content = content.replace("|", ""); - file.create(content.getBytes(), true, false, null); - AbstractTextEditor editor = (AbstractTextEditor) IDE.openEditor( - PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file, - "org.eclipse.ui.genericeditor.GenericEditor"); - editor.getSelectionProvider().setSelection(new TextSelection(offset, 0)); - LSContentAssistProcessor processor = new LSContentAssistProcessor(); - proposals = processor.computeCompletionProposals(Utils.getViewer(editor), offset); - DisplayHelper.sleep(editor.getSite().getShell().getDisplay(), 2000); - assertTrue(proposals.length > 1); - } - - @Test - public void autoCloseTags() throws Exception { - final IFile file = project.getFile("autoCloseTags.xml"); - file.create(""); - assertTrue( - DisplayHelper.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000, - () -> "".equals(document.get())), - "Autoclose not done"); - } - - /** - * Tests the creation of the system catalog with schemas that have been - * contributed. This tests as well the use of jar-URLs to allow relative - * includes within schemas (Issue #1078) - */ - @Test - public void testXMLCatalog() throws Exception { - // Create bundle with a catalog contribution - Path bundlePath = Files.createTempFile("xmlCatalogTest", "bundle.jar"); - bundlePath.toFile().deleteOnExit(); - - try (FileOutputStream bundleOutput = new FileOutputStream(bundlePath.toFile()); - JarOutputStream bundleJarOut = new JarOutputStream(bundleOutput)) { - - // Manifest - bundleJarOut.putNextEntry(new JarEntry("META-INF/MANIFEST.MF")); - PrintWriter fileOut = new PrintWriter(bundleJarOut, true, StandardCharsets.UTF_8); - fileOut.println("Manifest-Version: 1.0"); - fileOut.println("Bundle-ManifestVersion: 2"); - fileOut.println("Bundle-Name: XML-Catalog-Test"); - fileOut.println("Bundle-SymbolicName: org.eclipse.wildwebdeveloper.test.xmlcatalog;singleton:=true"); - fileOut.println("Bundle-Version: 0.0.1"); - fileOut.println("Require-Bundle: org.eclipse.wst.xml.core"); - - // plugin.xml - bundleJarOut.putNextEntry(new JarEntry("plugin.xml")); - fileOut = new PrintWriter(bundleJarOut, true, StandardCharsets.UTF_8); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - - // Schema in Jar - bundleJarOut.putNextEntry(new JarEntry("org/eclipse/wildwebdeveloper/test/schema.xsd")); - fileOut = new PrintWriter(bundleJarOut, true, StandardCharsets.UTF_8); - fileOut.println(""); - fileOut.println( - ""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - fileOut.println(""); - - bundleJarOut.closeEntry(); - } - - // Install and start bundle - Plugin plugin = org.eclipse.wildwebdeveloper.xml.internal.Activator.getDefault(); - BundleContext bundleContext = plugin.getBundle().getBundleContext(); - URL bundleUrl = bundlePath.toUri().toURL(); - Bundle catalogBundle = bundleContext.installBundle(bundleUrl.toExternalForm(), bundleUrl.openStream()); - catalogBundle.start(); - - // Open preferences dialog to trigger the refresh of the system catalog - PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn( - PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), - "org.eclipse.wildwebdeveloper.xml.internal.ui.preferences.XMLCatalogPreferencePage", null, null); - dialog.getShell().open(); - dialog.getShell().close(); - - // Find system.catalog in well known location from plugin - File systemCatalog = plugin.getStateLocation().append("system-catalog.xml").toFile(); - // Parse system-catalog to check it - Document systemCatalogDom = runWithLocalCatalogOnlyForTestXMLCatalog( - () -> DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().parse(systemCatalog)); - - // root - Node catalogNode = systemCatalogDom.getLastChild(); - assertEquals(Node.ELEMENT_NODE, catalogNode.getNodeType()); - assertEquals("catalog", catalogNode.getNodeName()); - - // find URI-entries - NodeList catalogEntries = catalogNode.getChildNodes(); - List uriNodes = IntStream.range(0, catalogEntries.getLength()).mapToObj(catalogEntries::item) - .filter(n -> n.getNodeType() == Node.ELEMENT_NODE).filter(n -> "uri".equals(n.getNodeName())) - .collect(Collectors.toList()); - assertFalse(uriNodes.isEmpty(), "uri-nodes expected"); - - // find expected entry - List expectedNodes = uriNodes.stream().filter(n -> "http://eclipse.org/wildwebdeveloper/test" - .equals(n.getAttributes().getNamedItem("name").getNodeValue())).collect(Collectors.toList()); - assertEquals(1, expectedNodes.size(), "one uri-node with the used name expected"); - Node uriNode = expectedNodes.get(0); - - // value of uri - assertNotNull(uriNode.getAttributes().getNamedItem("uri"), "uri-attribute expected"); - String uri = uriNode.getAttributes().getNamedItem("uri").getNodeValue(); - assertNotNull(uri, "value fro uri expected"); - // use of jar-uri - file is not cached in local filesystem. This enables - // relative includes in schemas - assertTrue(uri.startsWith("jar:file:/"), "jar-uri expected: " + uri); - assertTrue(uri.endsWith("/org/eclipse/wildwebdeveloper/test/schema.xsd"), - "relative path of schema in uri expected: " + uri); - - // Uninstall bundle once again - catalogBundle.uninstall(); - } - - /** - * Workaround for erratic runtime exceptions because of catalog.dtd temporarily not being available online: - *
{@code
-     *   java.io.FileNotFoundException: http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd
-     *       at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:2010)
-     *       at ...
-     *       at java.xml/javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:206)
-     *       at org.eclipse.wildwebdeveloper.tests.TestXML.testXMLCatalog(TestXML.java:246)
-     *       at java.base/java.lang.reflect.Method.invoke(Method.java:580)
-     *       at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
-     *       at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
-     * }
- */ - private T runWithLocalCatalogOnlyForTestXMLCatalog(Callable callable) throws Exception { - var origSysProps = new HashMap(); - List.of( // - "javax.xml.useCatalog", // - "javax.xml.catalog.files", // - "javax.xml.catalog.prefer", // - "javax.xml.catalog.resolve" - ).forEach(k -> origSysProps.put(k, System.getProperty(k))); - - var catalogRedirect = Path.of("src/org/eclipse/wildwebdeveloper/tests/catalog/oasis-catalog-redirect.xml"); - assertTrue(Files.exists(catalogRedirect)); - // enforce usage of local catalog.dtd file - System.setProperty("javax.xml.useCatalog", "true"); - System.setProperty("javax.xml.catalog.files", catalogRedirect.toUri().toString()); - System.setProperty("javax.xml.catalog.prefer", "system"); - System.setProperty("javax.xml.catalog.resolve", "strict"); - try { - return callable.call(); - } finally { - // restore previous behaviour - for (var e : origSysProps.entrySet()) { - if (e.getValue() == null) { - System.clearProperty(e.getKey()); - } else { - System.setProperty(e.getKey(), e.getValue()); - } - } - } - } + private IProject project; + private ICompletionProposal[] proposals; + + @BeforeEach + public void setUpProject() throws CoreException { + this.project = ResourcesPlugin.getWorkspace().getRoot().getProject(getClass().getName() + System.nanoTime()); + project.create(null); + project.open(null); + } + + @Test + public void testXMLFile() throws Exception { + final IFile file = project.getFile("blah.xml"); + file.create("FAIL".getBytes(), true, false, null); + ITextEditor editor = (ITextEditor) IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file); + editor.getDocumentProvider().getDocument(editor.getEditorInput()).set(" { + try { + return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0; + } catch (CoreException e) { + return false; + } + }), "Diagnostic not published"); + } + + @Test + public void testXSLFile() throws Exception { + final IFile file = project.getFile("blah.xsl"); + file.create("FAIL".getBytes(), true, false, null); + ITextEditor editor = (ITextEditor) IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file); + editor.getDocumentProvider().getDocument(editor.getEditorInput()).set("FAIL"); + assertTrue(DisplayHelper.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000, () -> { + try { + return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0; + } catch (CoreException e) { + return false; + } + }), "Diagnostic not published"); + } + + @Test + public void testXSDFile() throws Exception { + final IFile file = project.getFile("blah.xsd"); + file.create("FAIL".getBytes(), true, false, null); + ITextEditor editor = (ITextEditor) IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file); + editor.getDocumentProvider().getDocument(editor.getEditorInput()).set("a<"); + assertTrue(DisplayHelper.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000, () -> { + try { + return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0; + } catch (CoreException e) { + return false; + } + }), "Diagnostic not published"); + } + + @Test + public void testDTDFile() throws Exception { + final IFile file = project.getFile("blah.dtd"); + file.create("FAIL".getBytes(), true, false, null); + ITextEditor editor = (ITextEditor) IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file); + editor.getDocumentProvider().getDocument(editor.getEditorInput()).set(""); + assertTrue(DisplayHelper.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000, () -> { + try { + return file.findMarkers("org.eclipse.lsp4e.diagnostic", true, IResource.DEPTH_ZERO).length != 0; + } catch (CoreException e) { + return false; + } + }), "Diagnostic not published"); + } + + @Test + public void testComplexXML() throws Exception { + final IFile file = project.getFile("blah.xml"); + String content = "\n" + " |\n" + ""; + int offset = content.indexOf('|'); + content = content.replace("|", ""); + file.create(content.getBytes(), true, false, null); + AbstractTextEditor editor = (AbstractTextEditor) IDE.openEditor( + PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), + file, "org.eclipse.ui.genericeditor.GenericEditor"); + editor.getSelectionProvider().setSelection(new TextSelection(offset, 0)); + LSContentAssistProcessor processor = new LSContentAssistProcessor(); + proposals = processor.computeCompletionProposals(Utils.getViewer(editor), offset); + DisplayHelper.sleep(editor.getSite().getShell().getDisplay(), 2000); + assertTrue(proposals.length > 1); + } + + @Test + public void autoCloseTags() throws Exception { + final IFile file = project.getFile("autoCloseTags.xml"); + file.create(""); + assertTrue(DisplayHelper.waitForCondition(PlatformUI.getWorkbench().getDisplay(), 5000, () -> "".equals(document.get())), + "Autoclose not done"); + } + + /** + * Tests the creation of the system catalog with schemas that have been + * contributed. This tests as well the use of jar-URLs to allow relative + * includes within schemas (Issue #1078) + */ + @Test + public void testXMLCatalog() throws Exception { + // Create bundle with a catalog contribution + Path bundlePath = Files.createTempFile("xmlCatalogTest", "bundle.jar"); + bundlePath.toFile().deleteOnExit(); + + try (FileOutputStream bundleOutput = new FileOutputStream(bundlePath.toFile()); + JarOutputStream bundleJarOut = new JarOutputStream(bundleOutput)) { + + // Manifest + bundleJarOut.putNextEntry(new JarEntry("META-INF/MANIFEST.MF")); + PrintWriter fileOut = new PrintWriter(bundleJarOut, true, StandardCharsets.UTF_8); + fileOut.println("Manifest-Version: 1.0"); + fileOut.println("Bundle-ManifestVersion: 2"); + fileOut.println("Bundle-Name: XML-Catalog-Test"); + fileOut.println("Bundle-SymbolicName: org.eclipse.wildwebdeveloper.test.xmlcatalog;singleton:=true"); + fileOut.println("Bundle-Version: 0.0.1"); + fileOut.println("Require-Bundle: org.eclipse.wst.xml.core"); + + // plugin.xml + bundleJarOut.putNextEntry(new JarEntry("plugin.xml")); + fileOut = new PrintWriter(bundleJarOut, true, StandardCharsets.UTF_8); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + + // Schema in Jar + bundleJarOut.putNextEntry(new JarEntry("org/eclipse/wildwebdeveloper/test/schema.xsd")); + fileOut = new PrintWriter(bundleJarOut, true, StandardCharsets.UTF_8); + fileOut.println(""); + fileOut.println( + ""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + fileOut.println(""); + + bundleJarOut.closeEntry(); + } + + // Install and start bundle + Plugin plugin = org.eclipse.wildwebdeveloper.xml.internal.Activator.getDefault(); + BundleContext bundleContext = plugin.getBundle().getBundleContext(); + URL bundleUrl = bundlePath.toUri().toURL(); + Bundle catalogBundle = bundleContext.installBundle(bundleUrl.toExternalForm(), bundleUrl.openStream()); + catalogBundle.start(); + + // Open preferences dialog to trigger the refresh of the system catalog + PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), + "org.eclipse.wildwebdeveloper.xml.internal.ui.preferences.XMLCatalogPreferencePage", null, null); + dialog.getShell().open(); + dialog.getShell().close(); + + // Find system.catalog in well known location from plugin + File systemCatalog = plugin.getStateLocation().append("system-catalog.xml").toFile(); + // Parse system-catalog to check it + Document systemCatalogDom = runWithLocalCatalogOnlyForTestXMLCatalog(() -> DocumentBuilderFactory.newDefaultInstance() + .newDocumentBuilder().parse(systemCatalog)); + + // root + Node catalogNode = systemCatalogDom.getLastChild(); + assertEquals(Node.ELEMENT_NODE, catalogNode.getNodeType()); + assertEquals("catalog", catalogNode.getNodeName()); + + // find URI-entries + NodeList catalogEntries = catalogNode.getChildNodes(); + List uriNodes = IntStream.range(0, catalogEntries.getLength()).mapToObj(catalogEntries::item).filter(n -> n + .getNodeType() == Node.ELEMENT_NODE).filter(n -> "uri".equals(n.getNodeName())).collect(Collectors.toList()); + assertFalse(uriNodes.isEmpty(), "uri-nodes expected"); + + // find expected entry + List expectedNodes = uriNodes.stream().filter(n -> "http://eclipse.org/wildwebdeveloper/test".equals(n.getAttributes() + .getNamedItem("name").getNodeValue())).collect(Collectors.toList()); + assertEquals(1, expectedNodes.size(), "one uri-node with the used name expected"); + Node uriNode = expectedNodes.get(0); + + // value of uri + assertNotNull(uriNode.getAttributes().getNamedItem("uri"), "uri-attribute expected"); + String uri = uriNode.getAttributes().getNamedItem("uri").getNodeValue(); + assertNotNull(uri, "value fro uri expected"); + // use of jar-uri - file is not cached in local filesystem. This enables + // relative includes in schemas + assertTrue(uri.startsWith("jar:file:/"), "jar-uri expected: " + uri); + assertTrue(uri.endsWith("/org/eclipse/wildwebdeveloper/test/schema.xsd"), "relative path of schema in uri expected: " + uri); + + // Uninstall bundle once again + catalogBundle.uninstall(); + } + + /** + * Workaround for erratic runtime exceptions because of catalog.dtd temporarily not being available online: + * + *
{@code
+	 *   java.io.FileNotFoundException: http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd
+	 *       at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:2010)
+	 *       at ...
+	 *       at java.xml/javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:206)
+	 *       at org.eclipse.wildwebdeveloper.tests.TestXML.testXMLCatalog(TestXML.java:246)
+	 *       at java.base/java.lang.reflect.Method.invoke(Method.java:580)
+	 *       at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
+	 *       at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
+	 * }
+ */ + private T runWithLocalCatalogOnlyForTestXMLCatalog(Callable callable) throws Exception { + var origSysProps = new HashMap(); + List.of( // + "javax.xml.useCatalog", // + "javax.xml.catalog.files", // + "javax.xml.catalog.prefer", // + "javax.xml.catalog.resolve" // + ).forEach(k -> origSysProps.put(k, System.getProperty(k))); + + var catalogRedirect = Path.of("src/org/eclipse/wildwebdeveloper/tests/catalog/oasis-catalog-redirect.xml"); + assertTrue(Files.exists(catalogRedirect)); + // enforce usage of local catalog.dtd file + System.setProperty("javax.xml.useCatalog", "true"); + System.setProperty("javax.xml.catalog.files", catalogRedirect.toUri().toString()); + System.setProperty("javax.xml.catalog.prefer", "system"); + System.setProperty("javax.xml.catalog.resolve", "strict"); + try { + return callable.call(); + } finally { + // restore previous behaviour + for (var e : origSysProps.entrySet()) { + if (e.getValue() == null) { + System.clearProperty(e.getKey()); + } else { + System.setProperty(e.getKey(), e.getValue()); + } + } + } + } + + /** + * Verifies that HTTPS catalog URIs contributed via the WTP catalog + * are included in Wild Web Developer's generated system catalog. + * + * Expected behavior: the HTTPS URI is preserved in system-catalog.xml. + * Current behavior (issue #1892): WWD filters non file/jar schemes and + * drops the entry, so this test should fail until the code accepts http/https. + */ + @Test + public void testXMLCatalogHttpsUriIncluded() throws Exception { + final String HTTPS_NAME = "http://eclipse.org/wildwebdeveloper/https-test"; + final String HTTPS_URI = "https://jakarta.ee/xml/ns/jakartaee/beans_3_0.xsd"; + + // Create a bundle that contributes an HTTPS-based catalog entry + Path bundlePath = Files.createTempFile("xmlCatalogHttpsTest", "bundle.jar"); + bundlePath.toFile().deleteOnExit(); + + try (var bundleOutput = new FileOutputStream(bundlePath.toFile()); + var bundleJarOut = new JarOutputStream(bundleOutput)) { + + // Manifest + bundleJarOut.putNextEntry(new JarEntry("META-INF/MANIFEST.MF")); + bundleJarOut.write(""" + Manifest-Version: 1.0 + Bundle-ManifestVersion: 2 + Bundle-Name: XML-Catalog-HTTPS-Test + Bundle-SymbolicName: org.eclipse.wildwebdeveloper.test.xmlcatalog.https;singleton:=true + Bundle-Version: 0.0.1 + Require-Bundle: org.eclipse.wst.xml.core + """.getBytes(StandardCharsets.UTF_8)); + bundleJarOut.closeEntry(); + + // plugin.xml with HTTPS catalog contribution + bundleJarOut.putNextEntry(new JarEntry("plugin.xml")); + bundleJarOut.write(""" + + + + + + + + + + """.formatted(HTTPS_NAME, HTTPS_URI).getBytes(StandardCharsets.UTF_8)); + bundleJarOut.closeEntry(); + } + + // Install and start the bundle + Plugin plugin = org.eclipse.wildwebdeveloper.xml.internal.Activator.getDefault(); + BundleContext bundleContext = plugin.getBundle().getBundleContext(); + URL bundleUrl = bundlePath.toUri().toURL(); + Bundle catalogBundle; + try (var is = bundleUrl.openStream()) { + catalogBundle = bundleContext.installBundle(bundleUrl.toExternalForm(), is); + catalogBundle.start(); + } + + // Open preferences dialog to trigger the refresh of the system catalog + PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), + "org.eclipse.wildwebdeveloper.xml.internal.ui.preferences.XMLCatalogPreferencePage", null, null); + dialog.getShell().open(); + dialog.getShell().close(); + + // Find system-catalog and parse it + File systemCatalog = plugin.getStateLocation().append("system-catalog.xml").toFile(); + Document systemCatalogDom = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().parse(systemCatalog); + + // root element + Node catalogNode = systemCatalogDom.getLastChild(); + assertEquals(Node.ELEMENT_NODE, catalogNode.getNodeType()); + assertEquals("catalog", catalogNode.getNodeName()); + + // collect entries + NodeList catalogEntries = catalogNode.getChildNodes(); + List uriNodes = IntStream.range(0, catalogEntries.getLength()).mapToObj(catalogEntries::item).filter(n -> n + .getNodeType() == Node.ELEMENT_NODE).filter(n -> "uri".equals(n.getNodeName())).toList(); + assertFalse(uriNodes.isEmpty(), "uri-nodes expected"); + + // find contributed HTTPS entry + List expectedNodes = uriNodes.stream().filter(n -> HTTPS_NAME.equals(n.getAttributes().getNamedItem("name").getNodeValue())) + .toList(); + assertEquals(1, expectedNodes.size(), "one https uri-node with the used name expected"); + Node uriNode = expectedNodes.get(0); + + // ensure the https URI is preserved + assertNotNull(uriNode.getAttributes().getNamedItem("uri"), "uri-attribute expected"); + String uri = uriNode.getAttributes().getNamedItem("uri").getNodeValue(); + assertEquals(HTTPS_URI, uri, "https uri value should be preserved in catalog"); + + // cleanup + catalogBundle.uninstall(); + } } diff --git a/org.eclipse.wildwebdeveloper.xml/src/org/eclipse/wildwebdeveloper/xml/internal/ui/preferences/XMLCatalogs.java b/org.eclipse.wildwebdeveloper.xml/src/org/eclipse/wildwebdeveloper/xml/internal/ui/preferences/XMLCatalogs.java index d33a33eedb..7de8f71323 100644 --- a/org.eclipse.wildwebdeveloper.xml/src/org/eclipse/wildwebdeveloper/xml/internal/ui/preferences/XMLCatalogs.java +++ b/org.eclipse.wildwebdeveloper.xml/src/org/eclipse/wildwebdeveloper/xml/internal/ui/preferences/XMLCatalogs.java @@ -230,10 +230,11 @@ private static URI convertToURI(URL url) throws URISyntaxException, MalformedURL * @return true if supported, otherwise false */ private static boolean isSchemeSupportedInCatalog(URI uri) { - // are some other scheme supported directly by LemMinX ? - // LemminX-Issue for supported protocols: - // https://github.com/eclipse/lemminx/issues/1477 - return "file".equals(uri.getScheme()) || "jar".equals(uri.getScheme()); + // LemMinX supports resolving remote schemas; allow http/https here + // in addition to local schemes, so contributed catalog entries are + // preserved and not dropped (see issue #1892). + final String scheme = uri.getScheme(); + return "file".equals(scheme) || "jar".equals(scheme) || "http".equals(scheme) || "https".equals(scheme); } public static void storeUserCatalogs(IPreferenceStore store, Set catalogs) {