Skip to content
Melloware Home
GitHubStackOverflowLinkedIn

Java EE6-8 to Jakarta EE10 Upgrade

Java, Jakarta, JSF, Faces4 min read

While working on development projects for various clients, I often encounter the necessity to migrate legacy Java Enterprise Edition 6-8 applications to the latest Jakarta Enterprise Edition 10 standard. This transition involves a considerable amount of manual and repetitive renaming tasks, which can be prone to errors. I was initially considering creating my own tool for the job, but I was pleased to discover the OpenRewrite project had all the tools I needed.

Although OpenRewrite offered an impressive array of "recipes" for facilitating the migration to EE10, there were certain features missing in the EE10 migrations that I required. Notably, there was a lack of support for JSF a.k.a Java Server Faces (now referred to as Faces) projects. The OpenRewrite team graciously collaborated with me to submit, curate, and accept pull requests, addressing many of the missing recipes. This article will delve into most of these additions.

Running the migration recipe

To run the recipe from the command line, navigate to the root directory of your project and run:

1mvn -U org.openrewrite.maven:rewrite-maven-plugin:run
2 -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-migrate-java:RELEASE
3 -Drewrite.activeRecipes=org.openrewrite.java.migrate.jakarta.JakartaEE10

Let us look at some of the items this recipe will automatically update for you...

Java migration

The first thing that needs to happen is that all Java based code needs to be migrated.

  • All javax. packages will be upgraded to their jakarta. equivalent
  • Bean validation messages updated to {jakarta.validation.constraints}
  • CDI extension file renamed to \META-INF\services\jakarta.enterprise.inject.spi.Extension

Java EE6 Before:

1import java.math.BigDecimal;
2import javax.persistence.Column;
3import javax.persistence.Entity;
4import javax.persistence.Id;
5import javax.persistence.Table;
6import javax.validation.constraints.Digits;
7import javax.validation.constraints.NotNull;
8import javax.validation.constraints.Size;
9
10@Entity
11@Table(name = "DATA_LIMIT")
12public class DataLimit {
13
14 @Id
15 @Column(name = "DATA_LIMIT_ID")
16 private Long id;
17
18 @Column(name = "DATA_LIMIT_NAME")
19 @NotNull(message = "Data Limit Name {javax.validation.constraints.NotNull.message}")
20 @Size(max = 100, message = "Data Limit Name {javax.validation.constraints.Size.message}")
21 private String dataLimitName;
22
23 @Column(name = "MIN_VALUE")
24 @Digits(integer = 3, fraction = 3, message = "Minimum Value {javax.validation.constraints.Digits.message}")
25 @NotNull(message = "Minimum Value {javax.validation.constraints.NotNull.message}")
26 private BigDecimal minValue;
27}

Jakarta EE10 After:

1import java.math.BigDecimal;
2import jakarta.persistence.Column;
3import jakarta.persistence.Entity;
4import jakarta.persistence.Id;
5import jakarta.persistence.Table;
6import jakarta.validation.constraints.Digits;
7import jakarta.validation.constraints.NotNull;
8import jakarta.validation.constraints.Size;
9
10@Entity
11@Table(name = "DATA_LIMIT")
12public class DataLimit {
13
14 @Id
15 @Column(name = "DATA_LIMIT_ID")
16 private Long id;
17
18 @Column(name = "DATA_LIMIT_NAME")
19 @NotNull(message = "Data Limit Name {jakarta.validation.constraints.NotNull.message}")
20 @Size(max = 100, message = "Data Limit Name {jakarta.validation.constraints.Size.message}")
21 private String dataLimitName;
22
23 @Column(name = "MIN_VALUE")
24 @Digits(integer = 3, fraction = 3, message = "Minimum Value {jakarta.validation.constraints.Digits.message}")
25 @NotNull(message = "Minimum Value {jakarta.validation.constraints.NotNull.message}")
26 private BigDecimal minValue;
27}

XML schema migration

The XML schemas linked to EE files, including web.xml, beans.xml, faces-config.xml, and others, will undergo updates to align with the Jakarta EE namespace and version.

XML FileVersionSchema
web.xml6.0https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd
web-fragment.xml5.0https://jakarta.ee/xml/ns/jakartaee/web-fragment_5_0.xsd
beans.xml4.0https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd
faces-config.xml4.0https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_4_0.xsd
faces-taglib.xml4.0https://jakarta.ee/xml/ns/jakartaee/web-facelettaglibrary_4_0.xsd
persistence.xml3.0https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd

Below is an example of changes that will be made to a faces-config.xml after the recipe has run.

Java EE6 Before:

faces-config-1.0.xml
1<faces-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2 xmlns="http://java.sun.com/xml/ns/javaee"
3 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1.0.xsd"
4 version="1.0">
5 <render-kit>
6 <renderer>
7 <component-family>javax.faces.Output</component-family>
8 <renderer-type>javax.faces.Head</renderer-type>
9 <renderer-class>org.apache.myfaces.renderkit.html.HtmlHeadRenderer</renderer-class>
10 </renderer>
11 </render-kit>
12</faces-config>

Jakarta EE10 After:

faces-config-4.0.xml
1<faces-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2 xmlns="https://jakarta.ee/xml/ns/jakartaee"
3 xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_4_0.xsd"
4 version="4.0">
5 <render-kit>
6 <renderer>
7 <component-family>jakarta.faces.Output</component-family>
8 <renderer-type>jakarta.faces.Head</renderer-type>
9 <renderer-class>org.apache.myfaces.renderkit.html.HtmlHeadRenderer</renderer-class>
10 </renderer>
11 </render-kit>
12</faces-config>

Similar changes will be made to all the XML files listed in the table above!

Faces 4.0 migration

The most substantial modifications I required pertained to Faces files within the project. This encompasses the updating of all Java, XHTML, Javascript and Properties files that contain Faces-related code. The following list of items gets modified by the recipe:

  • The jsf/ClientSideSecretKey JNDI name has been renamed to faces/ClientSideSecretKey
  • Use jakarta.el instead of jakarta.faces.el and javax.faces.el
  • Substitutes Faces Managed Beans javax.faces.bean, which were deprecated in JavaServer Faces 2.3 and have been removed from Jakarta Faces 4.0 for jakarta.enterprise.context
  • Convert JSF to Faces values inside JavaScript,TypeScript, and Properties files such as window.jsf to window.faces and jsf.ajax to faces.ajax
  • Upgrade PrimeFaces to 13.x adding <classifier>jakarta</classifier>
  • Upgrade OmniFaces to 4.x and MyFaces to 4.x Jakarta versions
  • Convert XHTML pages to proper Jakarta namespaces and rename converters

Java EE6 Before:

snippet-jcp.xhtml
1<ui:composition
2 xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:f="http://java.sun.com/jsf/core"
4 xmlns:h="http://java.sun.com/jsf/html"
5 xmlns:ui="http://java.sun.com/jsf/facelets"
6 xmlns:c="http://java.sun.com/jsp/jstl/core"
7 xmlns:p="http://primefaces.org/ui"
8 xmlns:pe="http://primefaces.org/ui/extensions">
9<p:outputPanel id="container" layout="block">
10 <h:panelGrid columns="4">
11 <p:inputText converter="javax.faces.Long" value="#{controller.num}" />
12 </h:panelGrid>
13 <c:forEach items="#{controller.hoursOfDay}" var="hourOfDay" varStatus="status">
14 <pe:sheetcolumn headerText="#{hourOfDay}" colType="numeric">
15 <f:converter converterId="javax.faces.Integer"/>
16 </pe:sheetcolumn>
17 </c:forEach>
18</p:outputPanel>
19</ui:composition>

Jakarta EE10 After:

snippet-jakarta.xhtml
1<ui:composition
2 xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:f="jakarta.faces.core"
4 xmlns:h="jakarta.faces.html"
5 xmlns:ui="jakarta.faces.facelets"
6 xmlns:c="jakarta.tags.core"
7 xmlns:p="http://primefaces.org/ui"
8 xmlns:pe="http://primefaces.org/ui/extensions">
9<p:outputPanel id="container" layout="block">
10 <h:panelGrid columns="4">
11 <p:inputText converter="jakarta.faces.Long" value="#{controller.num}" />
12 </h:panelGrid>
13 <c:forEach items="#{controller.hoursOfDay}" var="hourOfDay" varStatus="status">
14 <pe:sheetcolumn headerText="#{hourOfDay}" colType="numeric">
15 <f:converter converterId="jakarta.faces.Integer"/>
16 </pe:sheetcolumn>
17 </c:forEach>
18</p:outputPanel>
19</ui:composition>

Conclusion

I hope you can see that utilizing this migration recipe should save significant time for developers when transitioning a legacy Java EE app to a Jakarta application. Should you encounter any notable issues that fail conversion, don't hesitate to reach out to me or report the issue on the OpenRewrite GitHub Issues repository. Working together we can ensure ongoing enhancements to this recipe!

© 2024 by Melloware. All rights reserved.