Uploaded image for project: 'CXF'
  1. CXF
  2. CXF-8836

Jaxb Integration with EclipseLink has Error

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Closed
    • Major
    • Resolution: Duplicate
    • 4.0.0
    • None
    • Core
    • None
    • Unknown

    Description

      Dear CXF developers,
      I am using version 4.0.0 of CXF cxf-spring-boot-starter-jaxws.
      My CXF jaxws services are wrapped in Spring Boot 3.0. I also use akarta.xml.bind-api 4.0.0 and org.eclipse.persistence.moxy 4.0.1.
      I generate Java models with cxf-codegen-plugin with the following configuration:

       

      <plugin>
          <groupId>org.apache.cxf</groupId>
          <artifactId>cxf-codegen-plugin</artifactId>
          <version>${cxf.version}</version>
          <dependencies>
              <dependency>
                  <groupId>org.jvnet.jaxb2_commons</groupId>
                  <artifactId>jaxb2-basics-annotate</artifactId>
                  <version>1.1.0</version>
              </dependency>
              <dependency>
                  <groupId>org.apache.cxf.xjc-utils</groupId>
                  <artifactId>cxf-xjc-runtime</artifactId>
                  <version>${cxf.version}</version>
              </dependency>
              <dependency>
                  <groupId>org.apache.cxf.xjcplugins</groupId>
                  <artifactId>cxf-xjc-ts</artifactId>
                  <version>${cxf.version}</version>
              </dependency>
              <dependency>
                  <groupId>javax.xml.bind</groupId>
                  <artifactId>jaxb-api</artifactId>
                  <version>2.4.0-b180830.0359</version>
              </dependency>
          </dependencies>
          <executions>
              <execution>
                  <id>generate-sources</id>
                  <phase>generate-sources</phase>
                  <configuration>
                      <sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
                      <defaultOptions>
                          <noAddressBinding>true</noAddressBinding>
                          <faultSerialVersionUID>3105839350746982386</faultSerialVersionUID>
                          <extraargs>
                              <extraarg>-xjc-Xannotate</extraarg>
                          </extraargs>
                      </defaultOptions>
                      <wsdlOptions>
                          <wsdlOption>
                              <wsdl>${basedir}/src/main/resources/wsdl/myservice.wsdl</wsdl>
                              <wsdlLocation>classpath:wsdl/myservice.wsdl</wsdlLocation>
                              <serviceName>myservice</serviceName>
                              <bindingFiles>
                                  <bindingFile>${basedir}/src/main/resources/wsdl/bindings.xjb</bindingFile>
                              </bindingFiles>
                          </wsdlOption>
                      </wsdlOptions>
                  </configuration>
                  <goals>
                      <goal>wsdl2java</goal>
                  </goals>
              </execution>
          </executions>
      </plugin>

       

      Binding file has the following configuration:

      <?xml version="1.0"?>
      <jaxb:bindings version="3.0"
                     xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"
                     jaxb:extensionBindingPrefixes="annox xjc"
                     xmlns:xs="http://www.w3.org/2001/XMLSchema"
                     xmlns:annox="http://annox.dev.java.net"
                     xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc">
          <jaxb:bindings>
              <jaxb:bindings>
                  <jaxb:globalBindings typesafeEnumMaxMembers="3000"
                                       generateElementProperty="false"
                                       mapSimpleTypeDef="false"
                                       fixedAttributeAsConstantProperty="true"
                                       choiceContentProperty="true"
                                       typesafeEnumBase="xs:boolean">
                  </jaxb:globalBindings>
              </jaxb:bindings>
          </jaxb:bindings>
          <jaxb:bindings schemaLocation="*">
              <jaxb:bindings node="//xs:complexType" multiple="true" required="false">
                  <annox:annotateClass>@lombok.experimental.SuperBuilder</annox:annotateClass>
                  <annox:annotateClass>@jakarta.xml.bind.annotation.XmlRootElement</annox:annotateClass>
                  <annox:annotateClass>@lombok.NoArgsConstructor</annox:annotateClass>
              </jaxb:bindings>
              <jaxb:bindings node="//xs:element" multiple="true" required="false">
                  <annox:annotate target="field">
                      @org.eclipse.persistence.oxm.annotations.XmlNullPolicy(nullRepresentationForXml=
                      XmlMarshalNullRepresentation.ABSENT_NODE, xsiNilRepresentsNull=false)
                  </annox:annotate>
              </jaxb:bindings>
          </jaxb:bindings>
      </jaxb:bindings>
      

       

      Model class generation works fine, nullable properties are marked with annotation:

      @XmlNullPolicy(nullRepresentationForXml = XmlMarshalNullRepresentation.ABSENT_NODE, xsiNilRepresentsNull = false)

      To start Moxy Runtime use the following settings in main method of Application:

       System.setProperty("jakarta.xml.bind.JAXBContextFactory", "org.eclipse.persistence.jaxb.JAXBContextFactory");

       
      I use this initialisation because I still need to use Spring Web Service Client from Spring WS in parallel.
       
      But on application startup I get errors:

      Caused by: java.lang.reflect.InvocationTargetException: null
          at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
          at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
          at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
          at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
          at org.apache.cxf.common.jaxb.JAXBUtils$3.run(JAXBUtils.java:1189) ~[cxf-core-4.0.0.jar:4.0.0]
          at org.apache.cxf.common.jaxb.JAXBUtils$3.run(JAXBUtils.java:1183) ~[cxf-core-4.0.0.jar:4.0.0]
          at java.base/java.security.AccessController.doPrivileged(AccessController.java:569) ~[na:na]
          at org.apache.cxf.common.jaxb.JAXBUtils.createContext(JAXBUtils.java:1183) ~[cxf-core-4.0.0.jar:4.0.0]
          ... 64 common frames omitted
      Caused by: jakarta.xml.bind.JAXBException: Property "eclipselink.default-target-namespace" is not supported.
          at org.glassfish.jaxb.runtime.v2.ContextFactory.createContext(ContextFactory.java:126) ~[jaxb-runtime-4.0.1.jar:4.0.1 - a95cb76]
          
      I have examined your code and found that you are using hard coded call to "Class<?> factoryClass = ClassLoaderUtils.loadClass("org.glassfish.jaxb.runtime.v2.ContextFactory" in cxf-core org.apache.cxf.common.jaxb.JAXBUtils

      I have experimented a bit and if you can make a few changes in two classes everything will work. The classes need to be changed:

      1. org.apache.cxf.common.jaxb.JAXBContextCache Method getCachedContextAndSchemas in lines 197 to 194 

          old code

       

      if (defaultNs != null) {
          if (HAS_MOXY){                 
            map.put("eclipselink.default-target-namespace", defaultNs);                 
          }
          map.put("org.glassfish.jaxb.defaultNamespaceRemap", defaultNs);
      }
      

          new code

      if (defaultNs != null){             
         if (HAS_MOXY){                 
            map.put("eclipselink.default-target-namespace", defaultNs);             
         }
         else{                 
            map.put("org.glassfish.jaxb.defaultNamespaceRemap", defaultNs);             
         }
      }
      

              
      2. org.apache.cxf.common.jaxb.JAXBUtils
          
          2.1 Method getPostfix in lines 1105 to 1117
              old code:

      private static String getPostfix(Class<?> cls) {
         String className = cls.getName();
         //TODO: review and remove "com.sun" package name check
         if (className.contains("org.glassfish.jaxb") || className.contains("com.sun.xml.bind")
                              || className.startsWith("com.ibm.xml")){                     
           return "";                 
         }
         else if (className.contains("com.sun.xml.internal")
                              || className.contains("eclipse")){                     
           //eclipse moxy accepts sun package CharacterEscapeHandler                     
            return ".internal";                 
          }
          return null;
       }
      

              new code:   

      private static String getPostfix(Class<?> cls) {
          String className = cls.getName();
          //TODO: review and remove "com.sun" package name check
          if (className.contains("org.glassfish.jaxb") || className.contains("com.sun.xml.bind")
                  || className.startsWith("com.ibm.xml") || className.contains("eclipse")) {
              //eclipse moxy 4.0 accepts glassfish package CharacterEscapeHandler see const SUN_CHARACTER_ESCAPE_HANDLER
              // Const in MOXY 4.0.1
              // SUN_CHARACTER_ESCAPE_HANDLER= "org.glassfish.jaxb.characterEscapeHandler";
              // SUN_JSE_CHARACTER_ESCAPE_HANDLER = "com.sun.xml.internal.bind.characterEscapeHandler";
              // SUN_CHARACTER_ESCAPE_HANDLER_MARSHALLER = "org.glassfish.jaxb.marshaller.CharacterEscapeHandler";
              // SUN_JSE_CHARACTER_ESCAPE_HANDLER_MARSHALLER = "com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler"
                          
              // part of setProperty(String key, Object value) from Class org.eclipse.persistence.jaxb. JAXBMarshaller
                          
              //        else if (SUN_CHARACTER_ESCAPE_HANDLER.equals(key) || SUN_JSE_CHARACTER_ESCAPE_HANDLER.equals(key) || 
              //                SUN_CHARACTER_ESCAPE_HANDLER_MARSHALLER.equals(key) || 
              //                SUN_JSE_CHARACTER_ESCAPE_HANDLER_MARSHALLER.equals(key)) {
              //        if (value == null) {
              //            xmlMarshaller.setCharacterEscapeHandler(null);
              //        } else {
              //            xmlMarshaller.setCharacterEscapeHandler(new CharacterEscapeHandlerWrapper(value));
              //        }
                          
              return "";
          } else if (className.contains("com.sun.xml.internal")) {
              return ".internal";
          }
          return null;
      }

                  
          2.2 Method createContext in lines 1179 to 1202
              old code:   

      public static JAXBContext createContext(final Set<Class<?>> classes,
                                                      final Map<String, Object> map) throws JAXBException {
        JAXBContext ctx = null;
        try {
            ctx = AccessController.doPrivileged(new PrivilegedExceptionAction<JAXBContext>() {
                public JAXBContext run() throws Exception {    
                    //This is a workaround for CXF-8675                            
      
                    Class<?> factoryClass = ClassLoaderUtils.loadClass("org.glassfish.jaxb.runtime.v2.ContextFactory",     
                              JAXBContextCache.class);                             
                    Method m = factoryClass.getMethod("createContext", Class[].class, Map.class);               
                    Object context = m.invoke(null, classes.toArray(new Class<?>[0]), map);                             
                    return (JAXBContext) context;                         
                }
            });
        } catch (PrivilegedActionException e2) {
            if (e2.getException() instanceof JAXBException){                         
                JAXBException ex = (JAXBException) e2.getException();                         
                throw ex;                     
            } else {                         
                throw new RuntimeException(e2.getException());                     
            }
        }
        return ctx;
      }
              
      

              new code:

      public static JAXBContext createContext(final Set<Class<?>> classes,
                                                  final Map<String, Object> map) throws JAXBException {
          JAXBContext ctx = null;
          try {
              ctx = AccessController.doPrivileged(new PrivilegedExceptionAction<JAXBContext>() {
                 public JAXBContext run() throws Exception {                             
                    String factoryClassName = classNameFromSystemProperties("org.glassfish.jaxb.runtime.v2.ContextFactory");                             
                    //This is a workaround for CXF-8675                             
                    Class<?> factoryClass = ClassLoaderUtils.loadClass(factoryClassName, JAXBContextCache.class);                             
                    Method m = factoryClass.getMethod("createContext", Class[].class, Map.class);                             
                    Object context = m.invoke(null, classes.toArray(new Class<?>[0]), map);                             
                    return (JAXBContext) context;                         
                 }
              });
          } catch (PrivilegedActionException e2) {
              if (e2.getException() instanceof JAXBException) {                         
                  JAXBException ex = (JAXBException) e2.getException();                         
                  throw ex;                     
              }
              else{                         
                  throw new RuntimeException(e2.getException());                     
              }
          }
          return ctx;
      }
      private static String classNameFromSystemProperties(String defaultValue) throws JAXBException {
          String factoryClassName = getSystemProperty(JAXBContext.JAXB_CONTEXT_FACTORY, defaultValue);
          if (factoryClassName != null){                     
              return factoryClassName;                 
          }
          return null;
      }
      private static String getSystemProperty(String property, String defaultValue) {
          String value = System.getProperty(property);
          if (value != null){                     
              return value;                 
          }
          else{                     
              return defaultValue;                 
          }
      }
      

      With this Changes work for me fine, without Errors. Can you please check if you can incorporate these changes into CXF-CORE.             

      If you have other possibilities to configure org.eclipse.persistence.moxy. Can you please describe. 
      Thanks in advance
      Anderas Rusjaev

       

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              andreas_rusjaev Andreas Rusjaev
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: