web-dev-qa-db-ja.com

純粋なJAX-WSを使用してSOAPヘッダーオブジェクトを追加

Paypal Express Checkout APIJAX WSを使用して、シンプルなWebサービスクライアントを実装しようとしています。 Paypal Express Checkout APIは、[〜#〜] wsdl [〜#〜]ファイルを提供します。 CXFのwsdl2Javaユーティリティを使用して、Javaクラスを生成できました。

認証の理由から、各リクエストにSOAPヘッダーを追加する必要があります。このヘッダーは非常に単純で、次のようになります。 https://cms.Paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_ECSOAPAPIBasics#id09C3I0CF0O6

[〜#〜] wsdl [〜#〜]から生成されたクラスには、ebay.apis.eblbasecomponents.CustomSecurityHeaderType各リクエストに追加する必要があるヘッダーを表すクラス。

したがって、質問は次のとおりです。CustomSecurityHeaderTypeクラスの手動で作成されたインスタンスをSOAP条件:

  1. ここでの回答に記載されているように、com.Sun。*パッケージのクラスを使用することはあまり望んでいません: JAX-WS-追加= SOAP Headers (主に異なるJDK間の移植性の問題の可能性があるため)
  2. そのオブジェクトをnestedjavax.xml.soap.SOAPElementインスタンスに手動でマーシャリングしたくないここの答えで述べたように、 SOAPを使用してヘッダーを追加する方法Java JAX-WS
19

だから、JAX-WS[〜#〜] jaxb [〜#〜を組み合わせて答えを見つけたようだ][〜#〜] so [〜#〜]からの関連する回答(これらの技術に精通した人がフォローが正しいかどうかを確認できれば本当にありがたいです):

私にとって明らかなことは、SOAPメッセージハンドラーを追加し、その中のSOAPMessageインスタンスのヘッダーを変更することです。

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);

ブックマーク#1の説明:ヘッダーオブジェクト自体をマーシャリングするのではなく、JAXBElementをマーシャリングする必要があります。そうしないと例外が発生します。ObjectFactoryのいずれかを使用する必要があります。クラスは[〜#〜] wsdl [〜#〜]から生成されます元のオブジェクトから必要なJAXBElementインスタンスを作成するため。 (答えてくれた@skaffmanに感謝: @ XmlRootElementはJAXBによって生成されません

これを拡張するMartin Straus answerも参照する必要があります

32

このソリューションはうまく機能しますが、問題があります。受信メッセージが処理されると、このエラーが生成されます。

dic 19, 2012 7:00:55 PM com.Sun.xml.messaging.saaj.soap.impl.EnvelopeImpl addHeader
SEVERE: SAAJ0120: no se puede agregar una cabecera si ya hay una
Exception in thread "main" javax.xml.ws.WebServiceException: Java.lang.RuntimeException: com.Sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
    at com.Sun.xml.ws.handler.ClientSOAPHandlerTube.callHandlersOnResponse(ClientSOAPHandlerTube.Java:167)
    at com.Sun.xml.ws.handler.HandlerTube.processResponse(HandlerTube.Java:174)
    at com.Sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.Java:1074)
    at com.Sun.xml.ws.api.pipe.Fiber._doRun(Fiber.Java:979)
    at com.Sun.xml.ws.api.pipe.Fiber.doRun(Fiber.Java:950)
    at com.Sun.xml.ws.api.pipe.Fiber.runSync(Fiber.Java:825)
    at com.Sun.xml.ws.client.Stub.process(Stub.Java:443)
    at com.Sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.Java:174)
    at com.Sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.Java:119)
    at com.Sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.Java:102)
    at com.Sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.Java:154)
    at $Proxy38.wsRdyCrearTicketDA(Unknown Source)
    at ar.com.fit.fides.remedy.api.ws.ServicioCreacionTickets.crearTicket(ServicioCreacionTickets.Java:55)
    at ar.com.fit.fides.remedy.api.ws.ConectorRemedyWS.crearTicket(ConectorRemedyWS.Java:43)
    at ar.com.fit.fides.remedy.api.ws.ConectorRemedyWS.main(ConectorRemedyWS.Java:90)
Caused by: Java.lang.RuntimeException: com.Sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
    at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.Java:50)
    at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.Java:23)
    at com.Sun.xml.ws.handler.HandlerProcessor.callHandleMessageReverse(HandlerProcessor.Java:341)
    at com.Sun.xml.ws.handler.HandlerProcessor.callHandlersResponse(HandlerProcessor.Java:214)
    at com.Sun.xml.ws.handler.ClientSOAPHandlerTube.callHandlersOnResponse(ClientSOAPHandlerTube.Java:161)
    ... 14 more
Caused by: com.Sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
    at com.Sun.xml.messaging.saaj.soap.impl.EnvelopeImpl.addHeader(EnvelopeImpl.Java:128)
    at com.Sun.xml.messaging.saaj.soap.impl.EnvelopeImpl.addHeader(EnvelopeImpl.Java:108)
    at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.Java:45)

したがって、解決策は、次のように送信メッセージの場合にメッセージが処理されているかどうかを確認することです。

public boolean handleMessage(SOAPMessageContext context) {
    try {
        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(AuthenticationInfo.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(info, soapHeader);
        }
    } catch (final Exception e) {
        throw new RuntimeException(e);
    }
    return true;
}
11
Martín Straus

次のようなヘッダーとしてparamsユーザーとパスワードを持つメソッドを公開するWebサービスを作成しました。

@WebService(serviceName="authentication")
public class WSAuthentication {
   String name = null;
   String password = null;

   public WSAuthentication() {
       super();
   }

   public WSAuthentication(String name, String password) {
       this.name = name;
       this.password = password;
   }

   private static String getData(WSAuthentication sec) {
       System.out.println("********************* AUTHENTICATION ********************" + "\n" + 
       "**********USER: " + sec.name + "\n" + 
       "******PASSWORD: " + sec.password + "\n" + 
       "******************************** AUTHENTICATION ****************************");
       return sec.name + " -- " + sec.password;
   }

   @WebMethod(operationName="security", action="authenticate")
   @WebResult(name="answer")
   public String security(@WebParam(header=true, mode=Mode.IN, name="user") String user, @WebParam(header=true, mode=Mode.IN, name="password") String password) {
        WSAuthentication secure = new WSAuthentication(user, password);
        return getData(secure);
     }
}

コンパイルして、WSDLクラスから生成されたテストを試してください。これがお役に立てば幸いです。

5
hekomobile

私はこの答えを見つけました:

JAX-WS-SOAPヘッダー)の追加

基本的に、コンパイラオプションに-XadditionalHeadersを追加し、ヘッダー内のオブジェクトは、メソッドのパラメーターとして生成されたコードに表示されます。

1
Radboud

Mavenを使用していて、jaxws-maven-pluginで必要なことは、xadditionalHeadersフラグをtrueに追加するだけです。入力としてヘッダーを持つメソッドを使用してクライアントが生成されます。

https://jax-ws-commons.Java.net/jaxws-maven-plugin/wsimport-mojo.html#xadditionalHeaders

1
Eduardo Dennis