So, it looks like I've found possible answer while combining JAX-WS & JAXB related answers from SO (I would really appreciate if somebody experienced in these technologies can check whether following is correct):
The obvious thing for me is to add SOAP message handler and alter header of SOAPMessage instance in it:
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.Handler;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.soap.SOAPHeader;
import ebay.api.paypalapi.ObjectFactory; // class generated by wsdl2java
// following class is generated by wsdl2java utility Service class
final PayPalAPIInterfaceService payPalService = new PayPalAPIInterfaceService();
final PayPalAPIAAInterface expressCheckoutPort = payPalService.getPayPalAPIAA();
final Binding binding = ((BindingProvider) expressCheckoutPort).getBinding();
List<Handler> handlersList = new ArrayList<Handler>();
// now, adding instance of Handler to handlersList which should do our job:
// creating header instance
final CustomSecurityHeaderType headerObj = new CustomSecurityHeaderType();
final UserIdPasswordType credentials = new UserIdPasswordType();
credentials.setUsername("username");
credentials.setPassword("password");
credentials.setSignature("signature");
headerObj.setCredentials(credentials);
// bookmark #1 - please read explanation after code
final ObjectFactory objectFactory = new ObjectFactory();
// creating JAXBElement from headerObj
final JAXBElement<CustomSecurityHeaderType> requesterCredentials = objectFactory.createRequesterCredentials(headerObj);
handlersList.add(new SOAPHandler<SOAPMessageContext>() {
@Override
public boolean handleMessage(final SOAPMessageContext context) {
try {
// checking whether handled message is outbound one as per Martin Strauss answer
final Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound");
if (outbound != null && outbound) {
// obtaining marshaller which should marshal instance to xml
final Marshaller marshaller = JAXBContext.newInstance(CustomSecurityHeaderType.class).createMarshaller();
// adding header because otherwise it's null
final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader();
// marshalling instance (appending) to SOAP header's xml node
marshaller.marshal(requesterCredentials, soapHeader);
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
return true;
}
// ... default implementations of other methods go here
});
// as per Jean-Bernard Pellerin's comment setting handlerChain list here, after all handlers were added to list
binding.setHandlerChain(handlersList);
Explanation of bookmark #1:
one should marshal not the header object itself, but JAXBElement representing that object, because otherwise one will get an exception. One should use one of ObjectFactory classes which are generated from WSDL for creating needed JAXBElement instances from original objects.
(Thanks @skaffman for answer: No @XmlRootElement generated by JAXB )
One should also refer to Martin Straus
answer which extends this one