Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Added support for Faces 4 changed xmlns namespaces #6096

Closed
wants to merge 2 commits into from

Conversation

asbachb
Copy link
Collaborator

@asbachb asbachb commented Jun 19, 2023

Since Jakarte EE Faces 4 the xmls namespaces were changed from http://xmlns.jcp.org/jsf to jakarta.*. (see jakartaee/faces#1553)

This change adds new namespace support for:
* Generation of new JSF pages
* Tag suggestion in faces xhtml editor view
* Automatic addition of missing namespace declaration when new taglib is used

In order to support the JSF 4.0 namespaces the enum JsfVersion in web.jsfapi module was introduced. It's used to map the taglib namespaces based on the projects detected JSF version.

Note: There's already an enum JSFVersion which is not part of the api. Both enums might be merged in the future.

Reference: #6069

asbachb added 2 commits June 19, 2023 19:52
Since Jakarte EE Faces 4 the xmls namespaces were changed from `http://xmlns.jcp.org/jsf` to `jakarta.*`.
(see jakartaee/faces#1553)

This change adds new namespace support for:
* Generation of new JSF pages
* Tag suggestion in faces xhtml editor view
* Automatic addition of missing namespace declaration when new taglib is used

In order to support the JSF 4.0 namespaces the `enum` `JsfVersion` in `web.jsfapi` module
was introduced. It's used to map the taglib namespaces based on the projects detected
JSF version.

Note: There's already an enum `JSFVersion` which is not part of the api. Both enums might
be merged in the future.

Reference: apache#6069
@morvael
Copy link

morvael commented Jun 20, 2023

What about enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/facelets/mojarra/ConfigManager.java file that is not able to load newer schemas? Not an expert in navigating github, but I can't see it amongst changed files. Sorry if this is stupid/irrelevant question.

@asbachb
Copy link
Collaborator Author

asbachb commented Jun 20, 2023

@morvael I don't know exactly what this class is doing. Actually I never worked in the NetBeans code base before.

I just created a new Maven WebApplication and tried to figure out what needs to be done to support the namespaces. As for that project type I'm quite sure how to setup.

I'd invite you to pull the PR build NetBeans and give it a try. If you encounter any further problems regarding the URN I can have a look into it.

@morvael
Copy link

morvael commented Jun 20, 2023

My problem was I didn't know from where to take new schemas, as the old DbfFactory didn't have them and the new one was completely reworked class (besides I would't know how to reference it). I'm not even aware what the class does, besides that it looks to be related to this topic (was mentioned in #5470).

I guess what everyone wants is for the Netbeans to:
a) recognize new namespaces and don't light up all red
b) offer code completion and navigation (so when you ctrl+click a bound property you're taken to view bean java code)

If your patch solves a & b then I'm OK with it, if only a) then it's not enough. Missing code completion and navigation is a big productivity killer in the last few versions of Netbeans when working with Jakarta 9+. Sadly, I'm a noob when it comes to fixing Netbeans code, as I would have most likely did this in my own free or even work time if I could do it.

@asbachb
Copy link
Collaborator Author

asbachb commented Jun 25, 2023

I built the branch for easier testing of interested folks which can be downloaded here: https://impl.it/NetBeans-dev-dev-1ebff966c4d7b3f98e208efd758a9fbdf90f0dbf-enterprise.zip

@pepness
Copy link
Member

pepness commented Jun 25, 2023

Hi @asbachb, using GlassFish 7.0.5 and Jakarta EE 10 I test the following:

  • Create a Ant Web App
  • Create a new JSF Page
  • Clean, build and run the web app

Without the PR the namespace is the oracle one:
Screenshot from 2023-06-25 17-13-47
With the PR the namespace is the Jakarta one:
Screenshot from 2023-06-25 17-14-17
Auto-completition still works and shows JSF 2.2 (which is correct because it is using web.jsf20 module)
Screenshot from 2023-06-25 17-09-56

How do I test Tag suggestion in faces xhtml editor view and Automatic addition of missing namespace declaration when new taglib is used. Also, I suggest that if possible you upload before/after images or step-to-step instructions to easily review the end result of your PR. Thank's for your hard work.

Copy link
Contributor

@matthiasblaesing matthiasblaesing left a comment

Choose a reason for hiding this comment

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

Thank you for working on this. I'm not an expert in J*EE, but I did a first pass - see comments inline.

@@ -1,5 +1,5 @@
#Signature file v4.1
#Version 1.54
#Version 1.55
Copy link
Contributor

Choose a reason for hiding this comment

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

Incompatible API/SPI changes normally require a bump of the major version of the spec. The enterprise modules were always lenitent with this, so I'm inclined to agree to keep it, but the right way would be:

  • bump major release version to 2
  • bump specification version to 2.0
  • update users to the new major release version and bump their specification version (compatible change)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm going to adjust this within the next review adjustments.

@@ -89,7 +90,7 @@ public LibraryType getType() {

@Override
public String getDefaultNamespace() {
return LibraryUtils.getCompositeLibraryURL(getLibraryName(), support.getJsfSupport().isJsf22Plus());
return LibraryUtils.getCompositeLibraryURL(getLibraryName(), support.getJsfSupport().getJsfVersion().isAtLeast(JsfVersion.JSF_2_2));
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm surprised. Did they really go to URNs and did not change the composite component namespace?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually I did not checked the context of that change tbh. I just wanted to get rid of the isJsf**Plus methods and use something more generic.
This was one call where such a method was used so I replaced it with the corresponding call.

return rootNode;
public static Node getRoot(HtmlParserResult parserResult, LibraryInfo library, JsfVersion jsfVersion) {
String namespace = jsfVersion.getNamespaceUri(library.getDefaultPrefix());
return parserResult.root(namespace);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this changes the semantic. The way this is written allows using both namespaces, while the new code only allows the "right" one. I would assume, that JSF implementations will recognize also old namespaces and I think the old behavior was sane.

You then can also keep the signature to its original form.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

From my understanding the old behavior only supported one namespace at a time as well.

e.g. when you create a new maven web application, add a new jsf site and add an tag which namespace is not yet present and you hit auto import you only can choose between the namespace of the used jsf version.

From my understanding if we want to provide all compatible namespaces for a jsf implementation this would be an feature which is currently not present.

Copy link
Contributor

Choose a reason for hiding this comment

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

I entered this into a facelets file:

<f:view
	xmlns="http://www.w3.org/1999/xhtml"
	xmlns:c="http://java.sun.com/jsp/jstl/core"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:p="http://primefaces.org/ui"
	xmlns:ui="http://java.sun.com/jsf/facelets">
	
	<h:head>
	</h:head>
	<h:body>
            <ui:define name="|" />
        </h:body>
</f:view>

I called completion at position marked with "|". At that point JsfUtils#getRoot is called with the DefaultLibraryInfo FACELETS. In my exampled I used the legacy namespace. Because the original method checks both namespaces it finds the f:view element, with your change that would have not happenend and an empty pseudo node had been returned.

So sorry, but your change introduces a bug, you need to scan all possible namespaces for the libraries.

map.put(JSFVersion.JSF_2_3, JsfVersion.JSF_2_3);
map.put(JSFVersion.JSF_3_0, JsfVersion.JSF_3_0);
map.put(JSFVersion.JSF_4_0, JsfVersion.JSF_4_0);
JSF_VERSION_MAPPING = Collections.unmodifiableMap(map);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason for the two version implementations?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not happy about that mapping as well.

Basically JSFVersion is not part of the api module but of implementation module web.jsf. So reusing JSFVersion would have meant either to:

  • Move it to web.jsfapi
    or
  • Add web.jsf as dependency

From my perspective it makes more sense to have it as part of the api, at least the feature set which is currently implemented. On the other hand I did not want to merge both classes as it would have created an even huger patchset than already.
So basically the idea would be to eliminate JSFVersion and integrate it to JsfVersion or somewhere else.

@@ -461,7 +462,7 @@ public static void completeFaceletsFromProject(CompletionContext context, List<C
public static void completeXMLNSAttribute(CompletionContext context, List<CompletionItem> items, JsfSupportImpl jsfs) {
if (context.getAttributeName().toLowerCase(Locale.ENGLISH).startsWith("xmlns")) { //NOI18N
//xml namespace completion for facelets namespaces
Set<String> nss = NamespaceUtils.getAvailableNss(jsfs.getLibraries(), jsfs.isJsf22Plus());
Set<String> nss = NamespaceUtils.getAvailableNss(jsfs.getLibraries(), jsfs.getJsfVersion().isAtLeast(JsfVersion.JSF_2_2));
Copy link
Contributor

Choose a reason for hiding this comment

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

I think getAvailableNss needs to be changed to take the version. The boolean is used to determine if only the current namespace or also legacy ones should be reported. With JSF4 this would add a third set:

  • >= JSF4 would get the new namespace + legacy jcp + legacy sun
  • >= JSF2.2 && < JSF4 would get legacy jcp + legacy sun
  • < JSF2.2 would get legacy sun

At least that is my interpretation from looking at it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I guess you're right.

Just for more context: Actually I was focusing on getting the functionality working mentioned in the isssue as I know what I can test. I did not do a all over the place, ensure that jsf 4 is supported refactoring.

I think this should be adjusted as well, but I'm not aware about the consequences and how to validate that behavior.

Copy link
Contributor

Choose a reason for hiding this comment

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

This method is invoced when you have this situation:

<f:view
	xmlns="http://www.w3.org/1999/xhtml"
	xmlns:c="http:|"
</f:view>

The caret/cursor is placed at the location marked with "|" and you call for completion (I have mapped that to CTRL+Space. You should get a list of supported libraries/namespaces (screenshot from a project based on JSF 2.2):

image

You can see, that both the legacy namespaces and the "jcp" namespaces are returned. The order should be reversed from my POV, but that is a separate discussion. In a JSF4 project I would expect all namespaces as already described.

@@ -246,15 +247,15 @@ private static void addTypesFromPackages(CompletionContext context, CompilationC
}
}

public static void completeSectionsOfTemplate(final CompletionContext context, final List<CompletionItem> items, String ns, OpenTag openTag) {
public static void completeSectionsOfTemplate(final CompletionContext context, final List<CompletionItem> items, String ns, OpenTag openTag, JsfVersion jsfVersion) {
Copy link
Contributor

Choose a reason for hiding this comment

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

See my comment in JsfUtils.getRoot: I think the final parameter is not necessary.

}

private void initialize() {
private void initialize(JsfVersion jsfVersion) {
Copy link
Contributor

Choose a reason for hiding this comment

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

See discussion in JsfUtils.getRoot I think this is not necessary.

if (jsfSupport != null && jsfSupport.isJsf22Plus()) {
ns = library.getNamespace();
}
ns = jsfVersion.getNamespaceUri(prefix);
Copy link
Contributor

Choose a reason for hiding this comment

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

The original code used the namespace from the library - should this not be possible anymore?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

With the new JsfVersion enum this is not necessary anymore as the namespace information is part of that enum.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is a nice addition to the codebase for providing flexible namespace support. Hopefully we will not be going through anymore namespace changes, but this will help in the future if it does happen.

@asbachb asbachb marked this pull request as draft June 30, 2023 08:45
@asbachb
Copy link
Collaborator Author

asbachb commented Jun 30, 2023

Converted back to draft for now as during the raised concerns I wonder if the suggested solution with a new mapping component JsfVersion makes that much sense.

NetBeans bundles javax.faces.jar and based on that META-INF information is generated. The old namespace is glued in with some quirks. I don't want to integrate the jakarta namespace the same quirky way but something with more substance.

@matthiasblaesing
Copy link
Contributor

NetBeans bundles javax.faces.jar and based on that META-INF information is generated. The old namespace is glued in with some quirks. I don't want to integrate the jakarta namespace the same quirky way but something with more substance.

What do you mean here? Can you point me to the generated code? A quick look only shows me the generation of javadoc in web.jsf.editor.

@asbachb
Copy link
Collaborator Author

asbachb commented Jul 2, 2023

@matthiasblaesing see https://github.com/apache/netbeans/blob/master/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/facelets/FaceletsLibrarySupport.java#L332-L419

It does not generate code but information based on the bundled JSF RI jar.

This code is base to generate the information used in FaceletsLibrary e.g. the namespace which is then used for stuff like auto import, code completion, etc.

This part tries to find a jsf implementation for the project:

  1. From some entry in web.xml
  2. From the classpath
  3. A bundled JSF implementation (based on glassfish reference implementation 2.3.9)

So the two problems I currently see (there might be more):

  1. Normally you develop only against the api so there's no JSF implementation on classpath so there's always a fallback to JSF 2.3
  2. Even if you have a JSF 4.0 implementation on classpath the taglib xmls cannot be parsed (see https://github.com/apache/netbeans/blob/master/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/facelets/mojarra/ConfigManager.java#L1062-L1154)

What I already tried:

  • Add Mojarra 4.0 Library -> Requires Java 11, currently not possible for enterprise cluster
  • Add ability to parse taglib.xml files with jakarta namespace -> Not possible when we want to to validation of the xml. The schema used for validation is bundled within the implementation which I cannot add

So currently I don't see another option beside mapping the namespaces manually as supposed. It's a shame as I think if we could get a JSF RI (e.g. it could be downloaded via maven) for the used JSF implementation it might work out of the box.

@morvael
Copy link

morvael commented Jul 5, 2023

2. Even if you have a JSF 4.0 implementation on classpath the taglib xmls cannot be parsed (see https://github.com/apache/netbeans/blob/master/enterprise/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/facelets/mojarra/ConfigManager.java#L1062-L1154)

Ah, so you have finally figured out what this stuff does. Too bad it's not possible to load new schemas from newer Mojarra as it requires JDK 11. Maybe they could be added locally to existing jars (just the schemas)?

@morvael
Copy link

morvael commented Jul 5, 2023

I built the branch for easier testing of interested folks which can be downloaded here: https://impl.it/NetBeans-dev-dev-1ebff966c4d7b3f98e208efd758a9fbdf90f0dbf-enterprise.zip

Tested my project. I seems most of the things I need are working (most important is navigation to bean). But:

a) since this window shows old library names I'm afraid it won't know about new attributes and/or elements, should any be added in newer JSF standards:
info1

b) taglib 4.0 is not recognized, if declaration remains in 2.2 version it works. Maybe I'm declaring it wrong, but xml passes validation:

<?xml version="1.0" encoding="UTF-8"?>
<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">

Other than that, great job, it's still a great improvement over old version!

@asbachb
Copy link
Collaborator Author

asbachb commented Jul 5, 2023

Ah, so you have finally figured out what this stuff does. Too bad it's not possible to load new schemas from newer Mojarra as it requires JDK 11. Maybe they could be added locally to existing jars (just the schemas)?

That would be a possibility. Another thing is that the module currently use some classes from the implementation itself. e.g. the schema is created based on a SchemaFactory provided by the JSF Implementation.

I guess I'll abandon this PR for now as I'm currently experimenting with another solution: I try to get the JSF implementation of the JSF version which is currently used via maven.
So for example if you use Jakarta EE 10 the module checks your local repository and if the JSF Reference Implementation is not present it will be downloaded from Maven Central. The module would still use the classes from the bundled JSF Implementation, but it would fetch the actual taglib descriptions from JSF reference implementation suited for the project.

That basically already works. Currently the question is how to handle the legacy namespaces. Per spec they're still valid. The problem is that these namespaces - at least I'm not aware of - could not be retrieved from the JSF reference implementation jar. So the module would need to define them manually and this needs a some more refactoring to do it properly.

@morvael
Copy link

morvael commented Jul 5, 2023

Please do not abandon it, something it better than nothing. We have been left out to dry for too long. You can make improved version for NB 20 or something. NB 19 would be awesome with just this PR.

@asbachb
Copy link
Collaborator Author

asbachb commented Jul 6, 2023

I'm actively working on an alternative solution, but I'm not 100% sure if this will hit NB19 as feature freeze will happen soon.

@asbachb
Copy link
Collaborator Author

asbachb commented Jul 7, 2023

This PR got replaced by #6160

@asbachb asbachb closed this Jul 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Java EE/Jakarta EE [ci] enable enterprise job
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants