javaweb-servicescxfcxf-client

Common request/response objects between 2 SOAP webservices in Java


In my project we must call 2 contract first SOAP Web Services with identical Request/Response objects.

As the web services were contract first, I created classes using CXF wsdl2java, now I have 2 set of identical classes in 2 packages. Is there a way I can refactor those classes into a set of classes.

To describe more I have 2 packages, each contain following classes, with exactly identical properties and codes:

@Getter
@Setter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"loginRequest", "request"})
@XmlRootElement(name = "requestHolder")
public final class RequestHolder {
    @XmlElement(required = true)
    private LoginRequest loginRequest;
    @XmlElement(required = true)
    private RequestData request;
}

and

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "LoginRequest", propOrder = {"username", "password"})
public final class LoginRequest {
    @NotBlank
    @XmlElement(required = true)
    private String username;
    @NotBlank
    @ToString.Exclude
    @XmlElement(required = true)
    private String password;
}

and

@Getter
@Setter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "requestData")
public final class RequestData {
    @XmlElement(required = true)
    private String statementId;
    @XmlElement(required = true)
    private String tracker;
    @XmlElement(required = true)
    private int depositCount;
    @XmlElement(required = true)
    private long depositAmount;
    @XmlElement(required = true)
    private int withdrawalCount;
    @XmlElement(required = true)
    private long withdrawalAmount;
    @XmlElement(required = true)
    private int unknownCount;
    @XmlElement(required = true)
    private long unknownAmount;
    @XmlElement(required = true)
    private int duplicateCount;
    @XmlElement(required = true)
    private long duplicateAmount;
    @XmlElement(required = true)
    private int okCount;
    @XmlElement(required = true)
    private long okAmount;
    @XmlElement(required = true)
    private String processCode;
    @XmlElement(required = true)
    private String processMessage;

}

and

@Getter
@Setter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"exception", "result"})
@XmlRootElement(name = "responseHolder")
public final class ResponseHolder {
    @XmlElement(name = "Exception", required = true)
    private ExceptionHolder exception;
    @XmlElement(name = "Result")
    private Result result;
}

and

@Getter
@Setter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "exceptionHolder", propOrder = {"code", "message"})
public final class ExceptionHolder {
    @XmlElement(name = "Code", required = true)
    private String code;
    @XmlElement(name = "Message", required = true)
    private String message;
}

and

@Getter
@Setter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "result")
public final class Result {
    @XmlElement(name = "Receipt", required = true, nillable = true)
    private String receipt;
}

And then I have 2 WebService interfaces which also like each other (bound to different end-points), with different WebService method names

@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@WebService(targetNamespace = Service1.TARGET_NAMESPACE, name = "Service1")
@XmlSeeAlso(ObjectFactory.class)
public interface Service1 {

    String TARGET_NAMESPACE = "http://www.mycompany.com/service1Namespace";

    @WebMethod
    @WebResult(name = "responseHolder", targetNamespace = TARGET_NAMESPACE, partName = "responseHolder")
    ResponseHolder serviceMethod1(
            @WebParam(partName = "requestHolder", name = "requestHolder", targetNamespace = TARGET_NAMESPACE) RequestHolder requestHolder);
}

and


@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@WebService(targetNamespace = Service2.TARGET_NAMESPACE, name = "Service2")
@XmlSeeAlso(ObjectFactory.class)
public interface Service2 {

    String TARGET_NAMESPACE = "http://www.anotherCompany.com/service2Namespace";

    @WebMethod
    @WebResult(name = "responseHolder", targetNamespace = TARGET_NAMESPACE, partName = "responseHolder")
    ResponseHolder serviceMethod2(
            @WebParam(partName = "requestHolder", name = "requestHolder", targetNamespace = TARGET_NAMESPACE) RequestHolder requestHolder);
}

As you can see only namespaces and service endpoints and method names are different. Besides these 2 service interfaces, there are 2 package-info.java files which are annotated with different namespaces.

When I tried to move the common classes to another package and use them, as they were not in the same package with annotated package-info.java files, their namespaces were not added correctly to the request envelopes, and I got errors at service endpoint.

So the question is, how can I use 1 set of request/response classes, is it possible at all?


Solution

  • It's a bit complex, but you can specify (when applying wsdl2java) a "binding file" that will allow you, via XJC, to customize the way Java classes are generated (eg the package names, the type mapping, etc...)

    In your particular case, you should look at "episode":

    A .episode file is generated by the XJC (XML Schema to Java) compiler. It is a schema bindings that associates schema types with existing classes. It is useful when you have one XML schema that is imported by other schemas as it prevents the model from being regenerated.

    (Old but) Interesting article:

    https://dzone.com/articles/reusing-generated-jaxb-classes