Common problems and troubleshooting

Some issues may appear during the development, testing, and native image building process of your quarkus-cxf project; below are some common ones and how to address them.

REST and SOAP Endpoints

Sometimes a REST endpoint may be needed in the same project where the Quarkus CXF Extension is used. The REST endpoint path must be different from the SOAP endpoint path (in order to avoid request forwarding conflicts between both protocols).

For example, if a WeatherWebService interface is declared in a WSDL, you can begin by creating the org.acme.cxf.WeatherWebServiceImpl class as follows:

package org.acme.cxf;

import ...

@Slf4j
@WebService(endpointInterface = "org.acme.cxf.WeatherWebService")
public class WeatherWebServiceImpl implements WeatherWebService {

    @Inject
    BackEndWeatherService backEndWeatherService;

    private Map<String, DailyTemperature> dailyTempByZipCode = Collections.synchronizedMap(new LinkedHashMap<>());

    public WeatherWebServiceImpl() {
        this.dailyTempByZipCode.addAll(
        		this.backEndWeatherService.getDailyForecast(Instant.now()));
    }

    @Override
    public DailyTemperature estimationTemperatures(String zipCode) {
        log.info("Daily estimation temperatures forecast called with '{}' zip code paramter", zipCode);
        return this.dailyTempByZipCode.get(zipCode);
    }
}

After that, you would need to specify the root context for your CXF web services, as indicated in the configuration documentation to split the REST (with RESTEasy for example) and SOAP routes based on their root context paths.

CXF’s SOAP properties:

quarkus.cxf.path=/soap
quarkus.cxf.endpoint."/weather".implementor=org.acme.cxf.WeatherWebServiceImpl

Now, imagine the following RESTEasy endpoint:

package org.acme.reasteasy;

import ...

@Slf4j
@Path("/healthcheck")
public class HealthCheckResource {

	@Inject
    BackEndWeatherService backEndWeatherService;

	@GET
	public Response doHealthCheck() {
		if(this.backEndWeatherService.isAvailable()) {
            return Response.ok().build();
		} else {
            return Response.status(Response.Status.SERVICE_UNAVAILABLE);
		}
	}
}

You can separate your REST endpoint by configuring the REASTEasy path:

quarkus.resteasy.path=/rest

You should now be able to send requests to both your REST and SOAP endpoints deployed within a single project, at:

Non ASCII Characters

Sometimes the wsdl2java autogenerated Java classes may not be fully compatible with GraalVM due to non ASCII characters getting included in the code. Similar exceptions to the below may appear during native image builds.

[quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26]      compile: 161 459,15 ms,  8,54 GB
[quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26]        image: 158 272,73 ms,  8,43 GB
[quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26]        write:     205,82 ms,  8,43 GB
Fatal error:com.oracle.svm.core.util.VMError$HostedError: java.lang.RuntimeException: oops : expected ASCII string! com.oracle.svm.reflect.OperationOrderStatusType_CRÉÉ_f151156b0d42ecdbdfb919501d8a86dda8733012_1456.hashCode
	at com.oracle.svm.core.util.VMError.shouldNotReachHere(VMError.java:72)

Below is an example of auto-generated non ASCII characters in a Java class:

@XmlType(name = "OperationOrderStatusType")
@XmlEnum
public enum OperationOrderStatusType {

    @XmlEnumValue("Cr\u00e9\u00e9")
    CRÉÉ("Cr\u00e9\u00e9"),
    @XmlEnumValue("A communiquer")
    A_COMMUNIQUER("A communiquer"),
    @XmlEnumValue("En attente de r\u00e9ponse")
    EN_ATTENTE_DE_RÉPONSE("En attente de r\u00e9ponse"),
    @XmlEnumValue("Attribu\u00e9")
    ATTRIBUÉ("Attribu\u00e9"),
    @XmlEnumValue("Clotur\u00e9")
    CLOTURÉ("Clotur\u00e9"),
    @XmlEnumValue("Annul\u00e9")
    ANNULÉ("Annul\u00e9");
    private final String value;

    OperationOrderStatusType(String v) {
        value = v;
    }

    public String value() {
        return value;
    }

    public static OperationOrderStatusType fromValue(String v) {
        for (OperationOrderStatusType c: OperationOrderStatusType.values()) {
            if (c.value.equals(v)) {
                return c;
            }
        }
        throw new IllegalArgumentException(v);
    }
}

Anything starting with \u will be a problem. Consequently the following refactoring is needed:

@XmlType(name = "OperationOrderStatusType")
@XmlEnum
public enum OperationOrderStatusType {

    @XmlEnumValue("Créé")
    CREE("Créé"),
    @XmlEnumValue("A communiquer")
    A_COMMUNIQUER("A communiquer"),
    @XmlEnumValue("En attente de réponse")
    EN_ATTENTE_DE_REPONSE("En attente de réponse"),
    @XmlEnumValue("Attribué")
    ATTRIBUE("Attribué"),
    @XmlEnumValue("Cloturé")
    CLOTURE("Cloturé"),
    @XmlEnumValue("Annulé")
    ANNULE("Annulé");
    private final String value;

    OperationOrderStatusType(String v) {
        value = v;
    }

    public String value() {
        return value;
    }

    public static OperationOrderStatusType fromValue(String v) {
        for (OperationOrderStatusType c: OperationOrderStatusType.values()) {
            if (c.value.equals(v)) {
                return c;
            }
        }
        throw new IllegalArgumentException(v);
    }
}