apache / netbeans

Apache NetBeans
https://netbeans.apache.org/
Apache License 2.0
2.64k stars 849 forks source link

Added support for JSF 4.0 namespaces #6160

Closed asbachb closed 1 year ago

asbachb commented 1 year ago

Previously NetBeans bundles the JSF reference implementation Mojarra in order to provide several functionality (like namespaces) based on the parsed taglib xml files from that jar. This is somehow problematic as: 1) The dependency needs to be updated on every new JSF release 2) NetBeans needs to fulfil the expectations of Mojarra

This leads to the problem that we currently cannot update and bundle the latest Mojarra version as Java 11 is required.

This change wants to get less dependant on that bundled JSF implementation by resolving the reference implementation using maven. Instead of reading the taglib xml files from that bundled jar NetBeans now gets in from the local maven repository or downloads it from maven central. If this is not possible we fallback to the bundled version.

This change also improves the situation when autosuggestion is invoked in the namespace part of JSF xhtml documents by ordering the suggested namespace with the one on top which is most likely to be used.

morvael commented 1 year ago

Will be glad to give it a spin, once a built version is available. I hope this solved taglib 4.0 files issue as well.

asbachb commented 1 year ago

The maven code might need some improvement. I'm not 100% sure if it respects the local maven settings (proxy, central repository override for company environments).

morvael commented 1 year ago

NB should offer access to its configured maven or at least the settings for it, no?

asbachb commented 1 year ago

@morvael Indeed, but I'm not 100% sure I got the correct API.

Created a preview build of this PR: https://impl.it/NetBeans-dev-dev-ddb8fbfc0816b3179736c22f91679f1ac434e491-enterprise.zip

morvael commented 1 year ago

Help works, it nicely suggests new namespace name when completing xmlns:ui=" info2

The only thing that is missing is recognizing taglib 4.0.

asbachb commented 1 year ago

@morvael Can you be a little bit more specific what you're missing?

morvael commented 1 year ago

re taglib

I have my own taglib registering a converter. It works as long as the declaration is set to:

<facelet-taglib version="2.2"
                xmlns="http://xmlns.jcp.org/xml/ns/javaee"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/javaee/web-facelettaglibrary_2_2.xsd">

It stops working when switched to

<facelet-taglib version="4.0"
                xmlns="https://jakarta.ee/xml/ns/jakartaee"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-facelettaglibrary_4_0.xsd">

Even though it's just like described in https://jakarta.ee/xml/ns/jakartaee/web-facelettaglibrary_4_0.xsd

Tag turns red and attributes are no longer recognizable, and the xhtml file using the tag is highlighted with red icon.

morvael commented 1 year ago

There's also this logged in the IDE log, might not be because of your change, but since it mentions something Jsf related I thought I'll include it: exception.txt

Not sure if this is related to this one following it: Even though the source level of .../src/main/java:.../src/main/webapp:.../src/main/resources is set to: 11, java.util.zip.CRC32C cannot be found on the system module path: I have IDE running on JDK17, project compiles with JDK17 though source/binary format is kept as 11. Might be unrelated NB bug :)

asbachb commented 1 year ago

@morvael May I ask you to provide an example project? Would be easier for me to figure out what's going on with that.

morvael commented 1 year ago

I'll try to make one. Give me some time.

morvael commented 1 year ago

Trying to make vanilla Web Application with Ant, but it seems it doesn't know about any JSF libraries and doesn't allow to switch from JSP to Facelets. Screenshot at 2023-07-07 09-36-26

morvael commented 1 year ago

Oops, a bug, seems in your new code (or it's old code): Screenshot at 2023-07-07 09-38-40 Cannot invoke "org.netbeans.modules.web.jsfapi.api.JsfVersion.isAtLeast(org.netbeans.modules.web.jsfapi.api.JsfVersion)" because "jsfVersion" is null

morvael commented 1 year ago

So, going with maven project then, at least there wizard can be completed.

asbachb commented 1 year ago

Oops, a bug, seems in your new code (or it's old code): Screenshot at 2023-07-07 09-38-40 Cannot invoke "org.netbeans.modules.web.jsfapi.api.JsfVersion.isAtLeast(org.netbeans.modules.web.jsfapi.api.JsfVersion)" because "jsfVersion" is null

That Exception is caused by the new code right. On the other hand I tried the same with NetBeans 18 which also seems not to work. So that Exception might new, but I guess the feature is already broken in NB18.

Edit: NB18 JSF 2.3 seems to work.

morvael commented 1 year ago

Lol, default maven web project does not compile as it pulls so old war plugin that it crashes without reflective access to some tree map comparator :) Switching project to use JDK11.

neilcsmith-net commented 1 year ago

This leads to the problem that we currently cannot update and bundle the latest Mojarra version as Java 11 is required.

I'm not the right person to be commenting on much of the JSF stuff, but this statement stands out. There may be other reasons for preferring the approach here, but if the right thing to do is to require Java 11 then that's the thing to do.

asbachb commented 1 year ago

This leads to the problem that we currently cannot update and bundle the latest Mojarra version as Java 11 is required.

I'm not the right person to be commenting on much of the JSF stuff, but this statement stands out. There may be other reasons for preferring the approach here, but if the right thing to do is to require Java 11 then that's the thing to do.

I slightly agree, but from my understanding this won't be fixed soon (see https://github.com/apache/netbeans/issues/4904).

Actually I think to use the matching JSF implementation does make much more sense, as things like attribute completion suggestions or helper text relies on that. Just proposing these attributes based on the latest or one most recent taglib description is not that ideal.

From my - limit - understanding I'd prefer to remove that bundled JSF implementation at all, but currently it's quite tightly coupled with the module.

asbachb commented 1 year ago

Lol, default maven web project does not compile as it pulls so old war plugin that it crashes without reflective access to some tree map comparator :) Switching project to use JDK11.

That's a known issue which is - afaik - fixed master. My branch is not rebased to the latest master branch.

To get it work with JDK 17 just update maven-war-plugin to most recent version then it should compile.

morvael commented 1 year ago

Crazy, after I made this project both 2.2 and 4.0 taglib declarations stopped to be recognized (both in sample project and my core project). But the project works once deployed. mavenproject1.tar.gz It's configured to use jdk called "JDK 11" and server "Wildfly", please change nb-configuration.xml directly if you need to change that. Should show something like this: Screenshot 2023-07-07 at 10-20-57 Facelet Title

neilcsmith-net commented 1 year ago

if the right thing to do is to require Java 11 then that's the thing to do.

I slightly agree, but from my understanding this won't be fixed soon (see #4904).

We have a few modules that require Java 11 - there are ways and means to work around this prior to all tests being fixed - if that is the right approach for this.

morvael commented 1 year ago

In vanilla NB 18 with old h namespace it can see my custom taglib (provided it's in 2.2 version): info3

morvael commented 1 year ago

I have upgraded plugin versions and switched it to use JDK_17, so here is latest version that works in vanilla NB 18 (so without taglib declaration using version 4.0): mavenproject1.tar.gz

Wildfly seems to be pretty forgiving, and accepts old namespaces etc - it's just NB that isn't developer friendly.

asbachb commented 1 year ago

@morvael Just pushed some fixes. Custom taglib should work now with 2.2 and 4.0.

Wildfly seems to be pretty forgiving, and accepts old namespaces etc - it's just NB that isn't developer friendly.

I don't think that's fair. If you want to improve the situation get behind the code and start hacking ;)

asbachb commented 1 year ago

Build: https://impl.it/NetBeans-dev-dev-ddb8fbfc0816b3179736c22f91679f1ac434e491-enterprise.zip

mbien commented 1 year ago

@asbachb could you hook enterprise/maven.j2ee, web.jsf and web.jsfapi into CI?

failing tests can be ignored by adding them to the: test.config.default.excludes= property in nbproject/project.properties of the module. If most of the test works but individual test cases fail I suppose you could comment them out.

web.jsf.editor has too many failures atm be worth the trouble - thanks

morvael commented 1 year ago

Build: https://impl.it/NetBeans-dev-dev-ddb8fbfc0816b3179736c22f91679f1ac434e491-enterprise.zip

Looks like it works, thank you!

morvael commented 1 year ago

I don't think that's fair. If you want to improve the situation get behind the code and start hacking ;)

I help the best way I can, by testing and commenting :) NB codebase scares me to do anything in it. I'm past my prime coder abilities time...

asbachb commented 1 year ago

@mbien done!

juneau001 commented 1 year ago

I have tested using the code in this PR and everything appears to be working very well. Editing existing JSF views within each Jakarta EE project type (8, 9, 10). Adding new JSF views within each project type also works well, creating the views with the correct namespaces for each of the corresponding Jakarta EE project types. I will look through the code and leave comments. Great work!

NicolaIsotta commented 1 year ago

Tried the dev build. I imported the settings from my current NB18 workspace. Got this exception while opening the projects:

Stack trace
java.lang.NullPointerException
    at org.netbeans.modules.web.jsfapi.api.NamespaceUtils.getForNs(NamespaceUtils.java:77)
    at org.netbeans.modules.web.jsf.editor.JsfSupportImpl.getLibrary(JsfSupportImpl.java:224)
    at org.netbeans.modules.web.jsf.editor.JsfPageMetadataProvider.getMetadataMap(JsfPageMetadataProvider.java:74)
    at org.netbeans.modules.web.common.api.WebPageMetadata.getMetadata(WebPageMetadata.java:54)
    at org.netbeans.modules.web.common.api.WebPageMetadata.getMetadata(WebPageMetadata.java:113)
    at org.netbeans.modules.web.common.api.WebPageMetadata.getContentMimeType(WebPageMetadata.java:87)
    at org.netbeans.modules.html.editor.HtmlErrorFilter.isErrorCheckingEnabledForMimetype(HtmlErrorFilter.java:147)
    at org.netbeans.modules.html.editor.hints.HtmlHintsProvider.computeErrors(HtmlHintsProvider.java:228)
    at org.netbeans.modules.html.editor.HtmlErrorFilter.filter(HtmlErrorFilter.java:82)
    at org.netbeans.modules.csl.core.ErrorFilterQuery.getFilteredErrors(ErrorFilterQuery.java:57)
    at org.netbeans.modules.csl.core.TLIndexerFactory$TLIndexer.index(TLIndexerFactory.java:255)
    at org.netbeans.modules.parsing.spi.indexing.Indexable$MyAccessor$3.run(Indexable.java:225)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater.runIndexer(RepositoryUpdater.java:274)
    at org.netbeans.modules.parsing.spi.indexing.Indexable$MyAccessor.index(Indexable.java:223)
[catch] at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Work$1T.run(RepositoryUpdater.java:3229)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Work$1T.run(RepositoryUpdater.java:3249)
    at org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask(TaskProcessor.java:586)
    at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:169)
    at org.netbeans.modules.parsing.api.ParserManager$MultiUserTaskAction.run(ParserManager.java:140)
    at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:181)
    at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:178)
    at org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager.priorityIO(FileChangedManager.java:153)
    at org.netbeans.modules.masterfs.providers.ProvidedExtensions.priorityIO(ProvidedExtensions.java:335)
    at org.netbeans.modules.parsing.nb.DataObjectEnvFactory.runPriorityIO(DataObjectEnvFactory.java:118)
    at org.netbeans.modules.parsing.impl.Utilities.runPriorityIO(Utilities.java:67)
    at org.netbeans.modules.parsing.impl.TaskProcessor.runUserTask(TaskProcessor.java:178)
    at org.netbeans.modules.parsing.api.ParserManager.parse(ParserManager.java:85)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Work.indexEmbedding(RepositoryUpdater.java:3268)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Work.doIndex(RepositoryUpdater.java:2861)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Work.lambda$index$0(RepositoryUpdater.java:2626)
    at org.netbeans.modules.parsing.impl.indexing.errors.TaskCache.refreshTransaction(TaskCache.java:540)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Work.index(RepositoryUpdater.java:2625)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Work.lambda$scanFiles$2(RepositoryUpdater.java:3332)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater.lambda$runInContext$4(RepositoryUpdater.java:2119)
    at org.openide.util.lookup.Lookups.executeWith(Lookups.java:288)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater.runInContext(RepositoryUpdater.java:2117)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater.runInContext(RepositoryUpdater.java:2098)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater.access$1400(RepositoryUpdater.java:135)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Work.scanFiles(RepositoryUpdater.java:3290)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$FileListWork.getDone(RepositoryUpdater.java:3832)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Work.doTheWork(RepositoryUpdater.java:3452)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Task._run(RepositoryUpdater.java:6197)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Task.access$3400(RepositoryUpdater.java:5855)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Task$2.lambda$call$0(RepositoryUpdater.java:6116)
    at org.openide.util.lookup.Lookups.executeWith(Lookups.java:288)
    at org.netbeans.modules.parsing.impl.RunWhenScanFinishedSupport.performScan(RunWhenScanFinishedSupport.java:83)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Task$2.call(RepositoryUpdater.java:6116)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Task$2.call(RepositoryUpdater.java:6112)
    at org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager.priorityIO(FileChangedManager.java:153)
    at org.netbeans.modules.masterfs.providers.ProvidedExtensions.priorityIO(ProvidedExtensions.java:335)
    at org.netbeans.modules.parsing.nb.DataObjectEnvFactory.runPriorityIO(DataObjectEnvFactory.java:118)
    at org.netbeans.modules.parsing.impl.Utilities.runPriorityIO(Utilities.java:67)
    at org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater$Task.run(RepositoryUpdater.java:6112)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1419)
    at org.netbeans.modules.openide.util.GlobalLookup.execute(GlobalLookup.java:45)
    at org.openide.util.lookup.Lookups.executeWith(Lookups.java:287)
    at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2034)

In JSF 4.0 project, seems like http://java.sun.com/jsf/core is not recognized anymore: image

I deleted the xmlns:f import and tried to fix imports with CTRL + SHIFT + I, but after confirming nothing happened. Found this stacktrace in the logs, not sure if it's related:

exception
SEVERE [org.openide.util.Exceptions]
java.util.NoSuchElementException
    at java.base/java.util.TreeMap$PrivateEntryIterator.nextEntry(TreeMap.java:1206)
    at java.base/java.util.TreeMap$KeyIterator.next(TreeMap.java:1262)
    at java.base/java.util.Collections$UnmodifiableCollection$1.next(Collections.java:1047)
    at org.netbeans.modules.web.jsf.editor.facelets.FaceletsLibrary.(FaceletsLibrary.java:53)
    at org.netbeans.modules.web.jsf.editor.facelets.CompositeComponentLibrary.(CompositeComponentLibrary.java:61)
    at org.netbeans.modules.web.jsf.editor.facelets.PureCompositeComponentLibrary.(PureCompositeComponentLibrary.java:31)
    at org.netbeans.modules.web.jsf.editor.facelets.FaceletsLibrarySupport.updateCompositeLibraries(FaceletsLibrarySupport.java:236)
    at org.netbeans.modules.web.jsf.editor.facelets.FaceletsLibrarySupport.getNamespaceLibraryMapping(FaceletsLibrarySupport.java:180)
    at org.netbeans.modules.web.jsf.editor.JsfSupportImpl.getLibrary(JsfSupportImpl.java:224)
    at org.netbeans.modules.web.jsf.editor.HtmlSourceTask.run(HtmlSourceTask.java:121)
    at org.netbeans.modules.web.jsf.editor.HtmlSourceTask.run(HtmlSourceTask.java:47)
    at org.netbeans.modules.parsing.impl.TaskProcessor.callParserResultTask(TaskProcessor.java:561)
[catch] at org.netbeans.modules.parsing.impl.TaskProcessor$RequestPerformer.run(TaskProcessor.java:786)
    at org.openide.util.lookup.Lookups.executeWith(Lookups.java:288)
    at org.netbeans.modules.parsing.impl.TaskProcessor$RequestPerformer.execute(TaskProcessor.java:702)
    at org.netbeans.modules.parsing.impl.TaskProcessor$CompilationJob.run(TaskProcessor.java:663)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1419)
    at org.netbeans.modules.openide.util.GlobalLookup.execute(GlobalLookup.java:45)
    at org.openide.util.lookup.Lookups.executeWith(Lookups.java:287)
    at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2034)

Good news: #4976 is fixed by this

asbachb commented 1 year ago

The old namespaces should be detected. I suspect it's an error caused by thee previous exception.

Can you share your project somehow?

LouisCollet commented 1 year ago

I also tested the dev build JSF 4 is working fine with the new namespaces (Ctrl-space) AND also JSF code completion is OK (see 5522) thanks for the modifications !! Have a nice day !! Louis configuration : java 19, wildfly 29.0.0.Beta1 with Mojarra 4.0.2, windows 10

asbachb commented 1 year ago

@NicolaIsotta The exception indicates that you might have a custom taglib without a namespace?!

If you could strip down the project to the essential files I can ensure that use case is handled properly.

NicolaIsotta commented 1 year ago

I'll try to create a shareable reproducer

asbachb commented 1 year ago

It seems like the namespace element for custom taglib components is optional which breaks the jsf functionality.

Based on the schema it's not optional. But if missing it breaks the entire functionality which should be prevented I guess.

asbachb commented 1 year ago

Added a null check and squashed as requested.

NicolaIsotta commented 1 year ago

reproducer.zip not sure why, but with this stripped down reproducer there's no exception, but it still complains it cannot find http://java.sun.com/jsf/core

asbachb commented 1 year ago

reproducer.zip not sure why, but with this stripped down reproducer there's no exception, but it still complains it cannot find http://java.sun.com/jsf/core

That's some copy and paste bug:

    JSF_CORE(
            sortedSet(
                    "jakarta.faces.core",
                    "http://xmlns.jcp.org/jsf/core",
                    "http://java.sun.com/jsf/html"
            ),
            "Jsf Core",
            "f"
    ), //NOI18N
asbachb commented 1 year ago

@NicolaIsotta If you want to can sent me the unstripped project privately. Just pushed a fix for that not recognized http://java.sun.com/jsf/core namespace.

NicolaIsotta commented 1 year ago

This should cause the exception: -EDITED-

asbachb commented 1 year ago

@NicolaIsotta Thanks for providing the project files. Was able to reproduce and fixed the behavior.

NicolaIsotta commented 1 year ago

Thank you for the PR. I think also #5470 will be fixed by this

asbachb commented 1 year ago

I found one location where I think composite components will not be found (see inline comment). I also noticed, that code completion for namespaces reports jakarata.faces.passthrough, jakarta.faces, jakarta.faces.composite/components/$COMPONENT even though the project is JSF 2.2.

Tbh I already recognized that as well, but haven't had the time to fix it.

One thing I tough about right know: What happens if the JSF artifacts are not present locally in the maven repository? Will they be downloaded and then what happens if there is connection to central possible?

So the idea would be: 1) Get the artifact from local repository 2) If not present download it from maven central 3) If this fails fallback to the bundled jsf implementation (which won't support jakarta namespaces)

3.) I have not tested actively.

asbachb commented 1 year ago

Pushed some more adjustment which

Just wanted to note: That this patchset might not be the silver bullet to support jakarta namespaces everywhere and is heavily influenced by the reported bugs. I think it would be a good idea to target NB19 to get some more feedback about potentially broken things with a clear way how to reproduce.

matthiasblaesing commented 1 year ago

@asbachb sorry, but I have to further things:

I still see jakarta composite libraries offered to me by CC. I looked into this an indeed they are generated independently of the JsfVersion. I suggest this:

Align `LibraryUtils#getCompositeLibraryURL` and `LibraryUtils#getAllCompositeLibraryNamespaces` ```diff # This patch file was generated by NetBeans IDE # It uses platform neutral UTF-8 encoding and \n newlines. --- a/enterprise/web.jsfapi/src/org/netbeans/modules/web/jsfapi/spi/LibraryUtils.java +++ b/enterprise/web.jsfapi/src/org/netbeans/modules/web/jsfapi/spi/LibraryUtils.java @@ -71,10 +71,14 @@ } } - public static Set getAllCompositeLibraryNamespaces(String libraryName) { + public static Set getAllCompositeLibraryNamespaces(String libraryName, JsfVersion jsfVersion) { Set namespaces = new LinkedHashSet<>(); - namespaces.add(COMPOSITE_LIBRARY_JAKARTA_NS + "/" + libraryName); - namespaces.add(COMPOSITE_LIBRARY_JCP_NS + "/" + libraryName); + if (jsfVersion.isAtLeast(JsfVersion.JSF_4_0)) { + namespaces.add(COMPOSITE_LIBRARY_JAKARTA_NS + "/" + libraryName); + } + if(jsfVersion.isAtLeast(JsfVersion.JSF_2_2)) { + namespaces.add(COMPOSITE_LIBRARY_JCP_NS + "/" + libraryName); + } namespaces.add(COMPOSITE_LIBRARY_SUN_NS + "/" + libraryName); return namespaces; } --- a/enterprise/web.jsfapi/nbproject/org-netbeans-modules-web-jsfapi.sig +++ b/enterprise/web.jsfapi/nbproject/org-netbeans-modules-web-jsfapi.sig @@ -279,7 +279,7 @@ meth public static java.lang.String getCompositeLibraryURL(java.lang.String,org.netbeans.modules.web.jsfapi.api.JsfVersion) meth public static java.util.Map getDeclaredLibraries(org.netbeans.modules.html.editor.lib.api.HtmlParsingResult) meth public static java.util.Map importLibrary(javax.swing.text.Document,java.util.Map) -meth public static java.util.Set getAllCompositeLibraryNamespaces(java.lang.String) +meth public static java.util.Set getAllCompositeLibraryNamespaces(java.lang.String,org.netbeans.modules.web.jsfapi.api.JsfVersion) meth public static org.netbeans.api.project.Project[] getOpenedJSFProjects() supr java.lang.Object ```

I also noticed, that the order of the namespaces did not seem consistent. I think the problem is in JsfNamespaceComparator. That nicely bundles the namespace based on prefix, but inside each group there is no order ensured by it.

Suggested adjustment to `JsfNamespaceComparator.java` ```diff --- a/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/facelets/JsfNamespaceComparator.java +++ b/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/facelets/JsfNamespaceComparator.java @@ -37,7 +37,12 @@ @Override public int compare(String namespace1, String namespace2) { - return rate(namespace1).compareTo(rate(namespace2)); + int prefixResult = rate(namespace1).compareTo(rate(namespace2)); + if(prefixResult != 0) { + return prefixResult; + } else { + return namespace1.compareTo(namespace2); + } } private Integer rate(String namespace) { ```
asbachb commented 1 year ago

I still see jakarta composite libraries offered to me by CC. I looked into this an indeed they are generated independently of the JsfVersion. I suggest this:

@matthiasblaesing can you give some more information on this (or a screenshot). I'm unable to reproduce that behavior.

Think I got it.

asbachb commented 1 year ago

@matthiasblaesing Applied suggested fixes.

matthiasblaesing commented 1 year ago

Before:

image

After:

image

The critical parts:

matthiasblaesing commented 1 year ago

@asbachb as I feared, in offline mode this was problematic. I removed the jsf-impl artifact from the local repository and cut the network connections. I got two error cases:

The first issue is, that the check if the maven artifact is present is not correct/incomplete. The maven resolver gives back a path, but the path point to a non-existing file. I got this exception:

Exception ``` WARNING [org.netbeans.modules.csl.editor.semantic.SemanticHighlighter]: SemanticAnalyzer = org.netbeans.modules.html.editor.gsf.HtmlSemanticAnalyzer@13bbb006; Language = org.netbeans.modules.csl.core.Language@541b0c9[text/html (mimetype = text/html; ParserResult = org.netbeans.modules.html.editor.api.gsf.HtmlParserResult$Lkp@66b7c8cd(mimepath = MimePath[text/xhtml/text/html]) java.lang.NullPointerException: Cannot invoke "org.openide.filesystems.FileObject.isValid()" because "fo" is null at org.openide.filesystems.JarArchiveRootProvider.isArchiveFile(JarArchiveRootProvider.java:72) at org.openide.filesystems.FileUtil.getArchiveRoot(FileUtil.java:1918) at org.netbeans.modules.web.jsf.editor.facelets.DefaultFaceletLibraries.init(DefaultFaceletLibraries.java:80) at org.netbeans.modules.web.jsf.editor.facelets.DefaultFaceletLibraries.(DefaultFaceletLibraries.java:67) at org.netbeans.modules.web.jsf.editor.facelets.FaceletsLibrarySupport.parseLibraries(FaceletsLibrarySupport.java:368) at org.netbeans.modules.web.jsf.editor.facelets.FaceletsLibrarySupport._findLibraries(FaceletsLibrarySupport.java:327) at org.netbeans.modules.web.jsf.editor.facelets.FaceletsLibrarySupport.findLibraries(FaceletsLibrarySupport.java:272) at org.netbeans.modules.web.jsf.editor.facelets.FaceletsLibrarySupport.getNamespaceLibraryMapping(FaceletsLibrarySupport.java:175) at org.netbeans.modules.web.jsf.editor.JsfSupportImpl.getLibrary(JsfSupportImpl.java:224) at org.netbeans.modules.web.jsf.editor.JsfPageMetadataProvider.getMetadataMap(JsfPageMetadataProvider.java:74) at org.netbeans.modules.web.common.api.WebPageMetadata.getMetadata(WebPageMetadata.java:54) at org.netbeans.modules.web.common.api.WebPageMetadata.getMetadata(WebPageMetadata.java:113) at org.netbeans.modules.web.common.api.WebPageMetadata.getContentMimeType(WebPageMetadata.java:87) at org.netbeans.modules.html.editor.gsf.HtmlSemanticAnalyzer.run(HtmlSemanticAnalyzer.java:60) ```

I suggest to adjust the contract of the provider to require the provider to only return valid paths.

Suggested fix ```diff --- a/enterprise/web.jsfapi/src/org/netbeans/modules/web/jsfapi/spi/JsfReferenceImplementationProvider.java +++ b/enterprise/web.jsfapi/src/org/netbeans/modules/web/jsfapi/spi/JsfReferenceImplementationProvider.java @@ -27,5 +27,11 @@ */ public interface JsfReferenceImplementationProvider { + /** + * Determine the path to the JSF reference implementation JAR. + * + * @param jsfVersion + * @return path to the JAR or {@code null} if not found + */ Path artifactPathFor(JsfVersion jsfVersion); } --- a/enterprise/maven.j2ee/src/org/netbeans/modules/maven/j2ee/MavenJsfReferenceImplementationProvider.java +++ b/enterprise/maven.j2ee/src/org/netbeans/modules/maven/j2ee/MavenJsfReferenceImplementationProvider.java @@ -18,11 +18,12 @@ */ package org.netbeans.modules.maven.j2ee; +import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Collections; import java.util.EnumMap; import java.util.Map; +import java.util.Optional; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactNotFoundException; @@ -82,6 +83,10 @@ return null; } - return Paths.get(jsfRIArtifact.getFile().toURI()); + return Optional.ofNullable(jsfRIArtifact) + .map(artifact -> artifact.getFile()) + .map(file -> file.toPath()) + .filter(file -> Files.exists(file)) + .orElse(null); } } ```

The second issue is that the schemas in the reference implementation JARs are not optimal. They contain this:

  <xsd:import namespace="http://www.w3.org/XML/1998/namespace"
          schemaLocation="http://www.w3.org/2001/xml.xsd"/>

The schema parse will then try to load the XSD from network. This is a privacy and performance problem. NetBeans has a solution for this: We carry the schemas and can use the NetBeans UserCatalog to resolve them. In offline mode I see an exception:

Exception ``` INFO [null]: Error parsing facelets library descriptor org.xml.sax.SAXParseException; systemId: jar:file:/home/matthias/src/netbeans/nbbuild/netbeans/enterprise/modules/ext/jsf-2_2/javax.faces.jar!/com/sun/faces/javaee_5.xsd; lineNumber: 182; columnNumber: 33; src-resolve: Name 'xml:lang' kann nicht als 'attribute declaration'-Komponente aufgelöst werden. at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:204) at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:135) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:396) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaErr(XSDHandler.java:4254) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaError(XSDHandler.java:4237) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getGlobalDecl(XSDHandler.java:1728) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDAttributeTraverser.traverseLocal(XSDAttributeTraverser.java:90) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDAbstractTraverser.traverseAttrsAndAttrGrps(XSDAbstractTraverser.java:760) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDComplexTypeTraverser.traverseSimpleContent(XSDComplexTypeTraverser.java:656) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDComplexTypeTraverser.traverseComplexTypeDecl(XSDComplexTypeTraverser.java:304) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDComplexTypeTraverser.traverseGlobal(XSDComplexTypeTraverser.java:191) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.traverseSchemas(XSDHandler.java:1480) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(XSDHandler.java:663) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(XMLSchemaLoader.java:618) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:577) at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:543) at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:281) at java.xml/javax.xml.validation.SchemaFactory.newSchema(SchemaFactory.java:612) at java.xml/javax.xml.validation.SchemaFactory.newSchema(SchemaFactory.java:644) at org.netbeans.modules.web.jsf.editor.facelets.mojarra.ConfigManager$ParseTask.getSchema(ConfigManager.java:1216) at org.netbeans.modules.web.jsf.editor.facelets.mojarra.ConfigManager$ParseTask.getDocument(ConfigManager.java:1140) at org.netbeans.modules.web.jsf.editor.facelets.mojarra.ConfigManager$ParseTask.call(ConfigManager.java:1045) Caused: com.sun.faces.config.ConfigurationException: Unable to parse document 'jar:file:/home/matthias/.m2/repository/org/primefaces/primefaces/11.0.0/primefaces-11.0.0.jar!/META-INF/primefaces-p.taglib.xml': src-resolve: Name 'xml:lang' kann nicht als 'attribute declaration'-Komponente aufgelöst werden. at org.netbeans.modules.web.jsf.editor.facelets.mojarra.ConfigManager$ParseTask.call(ConfigManager.java:1054) at org.netbeans.modules.web.jsf.editor.facelets.mojarra.ConfigManager$ParseTask.call(ConfigManager.java:971) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at org.netbeans.modules.web.jsf.editor.facelets.mojarra.ConfigManager.getConfigDocuments(ConfigManager.java:775) Caused: java.util.concurrent.ExecutionException at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191) [catch] at org.netbeans.modules.web.jsf.editor.facelets.mojarra.ConfigManager.getConfigDocuments(ConfigManager.java:792) at org.netbeans.modules.web.jsf.editor.facelets.FaceletsLibrarySupport.parseLibraries(FaceletsLibrarySupport.java:408) at org.netbeans.modules.web.jsf.editor.facelets.FaceletsLibrarySupport._findLibraries(FaceletsLibrarySupport.java:327) at org.netbeans.modules.web.jsf.editor.facelets.FaceletsLibrarySupport.findLibraries(FaceletsLibrarySupport.java:272) at org.netbeans.modules.web.jsf.editor.facelets.FaceletsLibrarySupport.getNamespaceLibraryMapping(FaceletsLibrarySupport.java:175) at org.netbeans.modules.web.jsf.editor.JsfSupportImpl.getLibrary(JsfSupportImpl.java:224) at org.netbeans.modules.web.jsf.editor.JsfPageMetadataProvider.getMetadataMap(JsfPageMetadataProvider.java:74) at org.netbeans.modules.web.common.api.WebPageMetadata.getMetadata(WebPageMetadata.java:54) at org.netbeans.modules.web.common.api.WebPageMetadata.getMetadata(WebPageMetadata.java:113) at org.netbeans.modules.web.common.api.WebPageMetadata.getContentMimeType(WebPageMetadata.java:87) at org.netbeans.modules.html.editor.gsf.HtmlSemanticAnalyzer.run(HtmlSemanticAnalyzer.java:60) at org.netbeans.modules.csl.editor.semantic.SemanticHighlighter.process(SemanticHighlighter.java:278) at org.netbeans.modules.csl.editor.semantic.SemanticHighlighter.access$000(SemanticHighlighter.java:57) at org.netbeans.modules.csl.editor.semantic.SemanticHighlighter$1.run(SemanticHighlighter.java:108) at org.netbeans.modules.csl.editor.semantic.SemanticHighlighter$1.run(SemanticHighlighter.java:117) at org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask(TaskProcessor.java:586) at org.netbeans.modules.parsing.api.ParserManager$UserTaskAction.run(ParserManager.java:132) at org.netbeans.modules.parsing.api.ParserManager$UserTaskAction.run(ParserManager.java:116) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:181) at org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:178) at org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager.priorityIO(FileChangedManager.java:153) at org.netbeans.modules.masterfs.providers.ProvidedExtensions.priorityIO(ProvidedExtensions.java:335) at org.netbeans.modules.parsing.nb.DataObjectEnvFactory.runPriorityIO(DataObjectEnvFactory.java:118) at org.netbeans.modules.parsing.impl.Utilities.runPriorityIO(Utilities.java:67) at org.netbeans.modules.parsing.impl.TaskProcessor.runUserTask(TaskProcessor.java:178) at org.netbeans.modules.parsing.api.ParserManager.parse(ParserManager.java:83) at org.netbeans.modules.csl.editor.semantic.SemanticHighlighter.run(SemanticHighlighter.java:98) at org.netbeans.modules.csl.editor.semantic.SemanticHighlighter.run(SemanticHighlighter.java:57) at org.netbeans.modules.parsing.impl.TaskProcessor.callParserResultTask(TaskProcessor.java:561) at org.netbeans.modules.parsing.impl.TaskProcessor$RequestPerformer.run(TaskProcessor.java:786) at org.openide.util.lookup.Lookups.executeWith(Lookups.java:288) at org.netbeans.modules.parsing.impl.TaskProcessor$RequestPerformer.execute(TaskProcessor.java:702) at org.netbeans.modules.parsing.impl.TaskProcessor$CompilationJob.run(TaskProcessor.java:663) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1419) at org.netbeans.modules.openide.util.GlobalLookup.execute(GlobalLookup.java:45) at org.openide.util.lookup.Lookups.executeWith(Lookups.java:287) at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2034) INFO [null]: Error parsing facelets library descriptor ```
Suggested fix ```diff # This patch file was generated by NetBeans IDE # It uses platform neutral UTF-8 encoding and \n newlines. --- a/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/facelets/mojarra/ConfigManager.java +++ b/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/facelets/mojarra/ConfigManager.java @@ -68,6 +68,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.Reader; +import java.io.StringWriter; import java.lang.annotation.Annotation; import java.lang.ref.WeakReference; import java.net.MalformedURLException; @@ -120,9 +122,13 @@ import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; +import org.netbeans.api.xml.services.UserCatalog; import org.netbeans.modules.web.jsf.editor.facelets.DefaultFaceletLibraries; import org.netbeans.modules.web.jsfapi.api.JsfNamespaces; +import org.openide.util.Exceptions; import org.w3c.dom.*; +import org.w3c.dom.ls.LSInput; +import org.w3c.dom.ls.LSResourceResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; @@ -1180,6 +1186,31 @@ WeakReference schema = SCHEMA_CACHE.get(id); if (schema == null || schema.get() == null) { SchemaFactory schemaFactory = SchemaFactory.newDefaultInstance(); + schemaFactory.setResourceResolver(new LSResourceResolver() { + @Override + public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) { + try { + InputSource is = UserCatalog.getDefault(). + getEntityResolver(). + resolveEntity(publicId, systemId); + if (is != null) { + return new LSInputFromInputSource(is); + } + } catch (SAXException | IOException ex) { + LOGGER.log( + Level.FINE, + "Failed to resolve namespaceURI: {}, publicId: {}, systemId: {}, baseURI: {}", + new Object[] { + namespaceURI, + publicId, + systemId, + baseURI + } + ); + } + return null; + } + }); schema = new WeakReference<>(schemaFactory.newSchema(jsfRIClassLoader.getResource(schemaResourceName))); SCHEMA_CACHE.put(id, schema); @@ -1345,4 +1376,96 @@ } // END URITask + /** + * Helperclass to supply the SchemaFactory with XSDs from the NB catalog + */ + private static class LSInputFromInputSource implements LSInput { + + private final InputSource is; + + public LSInputFromInputSource(InputSource is) { + this.is = is; + } + + @Override + public Reader getCharacterStream() { + return is.getCharacterStream(); + } + + @Override + public void setCharacterStream(Reader characterStream) { + } + + @Override + public InputStream getByteStream() { + return is.getByteStream(); + } + + @Override + public void setByteStream(InputStream byteStream) { + } + + @Override + public String getStringData() { + try (Reader r = getCharacterStream()) { + if (r == null) { + return null; + } + StringWriter sw = new StringWriter(); + getCharacterStream().transferTo(sw); + return sw.toString(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + @Override + public void setStringData(String stringData) { + } + + @Override + public String getSystemId() { + return is.getSystemId(); + } + + @Override + public void setSystemId(String systemId) { + } + + @Override + public String getPublicId() { + return is.getPublicId(); + } + + @Override + public void setPublicId(String publicId) { + } + + @Override + public String getBaseURI() { + return ""; + } + + @Override + public void setBaseURI(String baseURI) { + } + + @Override + public String getEncoding() { + return is.getEncoding(); + } + + @Override + public void setEncoding(String encoding) { + } + + @Override + public boolean getCertifiedText() { + return false; + } + + @Override + public void setCertifiedText(boolean certifiedText) { + } + } } ```
asbachb commented 1 year ago

@matthiasblaesing applied fixes (slightly adjusted stream expressions)

Note: Based on the Stack it seems that this exception is not coming from the standard taglibs, but from primefaces: primefaces-11.0.0.jar.