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:
-
http://localhost:8080/soap/weather for SOAP
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);
}
}