Showing posts with label webservices. Show all posts
Showing posts with label webservices. Show all posts

Tuesday, December 3, 2013

Creating async request reply web service over jms on WebSphere (SOAP over JMS)

Introduction

I started with this developerworks tutorial and its files.

It has a section Asynchronous JAX-WS Web service invocations with SOAP over JMS, but the article is not doing any implementation, it is just a theoretical explanation. Let me show you how to call the web service asynchronously using soap over jms.

Software used:
  • IBM Integration Designer 7.5.1 
  • IBM Process Server 7.5.1.0 on WAS 7.0.0.19

Service code generation

Generate your server code as described in the tutorial. Use "Top down EJB Web Service" to create a soap over jms service. The same service code is used for both sync and async calls.

Client code generation

Right click the wsdl file in RAD or WID, and select web services > generate client

Make sure you flag "enable asynchronous invocation for generated client"



Now in the client code, the *Async operations are important. Three methods are generated for each request reply operation in the wsdl.

This wsdl does not contain one way (fire and forget) operations, but if it did, it would result in one method in the client code. Because one way operations are alway async when using JMS, they have only one method in the generated class.

One method (without Async postfix) is for sync calling, one returns a response handler (for polling), and the third accepts a callbackhandler.

The standard synchronous method

public EchoStringResponse echoOperation(EchoStringInput parameter);

The asynchronous method with polling

public Response<EchoStringResponse> echoOperationAsync(EchoStringInput parameter);

The asynchronous method with callback

public Future<?> echoOperationAsync(EchoStringInput parameter, AsyncHandler<EchoStringResponse> asyncHandler);

Calling with response

Use the response wrapper to check if the async call is finished. While it is not finished, other tasks can be executed.

Response<EchoStringResponse> asyncresp = service.getEchoServicePort().echoOperationAsync(input);

while (!asyncresp.isDone()){
    System.out.println("no response yet, let's have a nap");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {

         System.out.println("failed to nap :(");
    }
}
try {
    System.out.println("I got a response! request send: " + request.getParameter("input") + ", got response : " + asyncresp.get().getEchoResponse());
} catch (Exception e) {
    e.printStackTrace();
}

Calling with callbackhandler

You can do the same with a callbackhandler, if it is not necessary to process the response in the same thread.

input.setEchoInput(input.getEchoInput() + " with callback");

service.getEchoServicePort().echoOperationAsync(input, new AsyncHandler<EchoStringResponse>() {

    @Override
    public void handleResponse(Response<EchoStringResponse> res) {

        try {
            System.out.println("I got a callback response! " + res.get().getEchoResponse());
        } catch (Exception e) {
            e.printStackTrace();

        }
    }});

Resources

This article in the infocenter talks about setting JVM properties for the async message listener, but I don't really understand what it is about, or why you should set these properties, it works fine without them, just define a reply queue: http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.nd.multiplatform.doc/info/ae/ae/twbs_jmsasyncresplistener.html

More info on Invoking JAX-WS Web services asynchronously: http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.base.doc/info/aes/ae/twbs_jaxwsclientasync.html

It makes the following statement:
To enable asynchronous mappings, you can add the jaxws:enableAsyncMapping binding declaration to the WSDL file.

I added that to the wsdl, and after I did the generated client will ALWAYS had the asynchronous method, even when I didn't check "enable asynchronous invocation for generated client" in the client generation wizard.


code:
<jaxws:bindings>
    <jaxws:enableAsyncMapping>true</jaxws:enableAsyncMapping>
</jaxws:bindings>

Don't forget to add the namespace declaration to the wsdl


code:
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"


If the server code is generated with the enableAsyncMapping extension added to the wsdl, you will find that also in the web service implementation class async methods have been generated

public Response<EchoStringResponse> echoOperationAsync(EchoStringInput parameter){
    // TODO Auto-generated method stub
    return null;
}

These are not valid at server side. You will get a stacktrace like this in the systemout :

[2/12/13 11:28:41:442 CET] 00000083 JMSListenerMD E   WSWS3018E: Caught exception during request processing: WSWS3163E: Error: The Web services engine could not find a target service to invoke!  targetService is EchoServicePortType
[2/12/13 11:28:42:173 CET] 0000007f SystemErr     R java.util.concurrent.ExecutionException: javax.xml.ws.soap.SOAPFaultException: WSWS3163E: Error: The Web services engine could not find a target service to invoke!  targetService is EchoServicePortType
[2/12/13 11:28:42:173 CET] 0000007f SystemErr     R         at org.apache.axis2.jaxws.client.async.AsyncResponse.onError(AsyncResponse.java:141)
[2/12/13 11:28:42:173 CET] 0000007f SystemErr     R         at org.apache.axis2.jaxws.client.async.PollingFuture.onError(PollingFuture.java:95)
[2/12/13 11:28:42:173 CET] 0000007f SystemErr     R         at org.apache.axis2.description.OutInAxisOperationClient$NonBlockingInvocationWorker.run(OutInAxisOperation.java:470)
[2/12/13 11:28:42:173 CET] 0000007f SystemErr     R         at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:897)
[2/12/13 11:28:42:173 CET] 0000007f SystemErr     R         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:919)
[2/12/13 11:28:42:174 CET] 0000007f SystemErr     R         at java.lang.Thread.run(Thread.java:736)


You can remove these *Async postfixed methods in the implementation and interface to resolve this issue.

Monday, April 22, 2013

Encrypt Message Using WS-Security in Websphere 7: InvalidKeyException

I tried to encrypt a soap message using WS-Security with an SSL certificate I got from the service provider.

My runtime is a WebSphere ESB 7.5.1.1 running on WAS 7.0.0.27

After configuring the policy set and the client policy set binding, I couldn't get rid of the following exception when executing the web service client:

Exception:
javax.xml.ws.WebServiceException:
com.ibm.wsspi.wssecurity.core.SoapSecurityException: CWWSS5612E: Encrypting the
data produced the following exception: Wrong length: 162:
java.security.InvalidKeyException: Wrong length: 162 Message: com.ibm.wsspi.wssecurity.core.SoapSecurityException:
CWWSS5612E: Encrypting the data produced the following exception: Wrong length:
162: java.security.InvalidKeyException: Wrong length: 162

After trying a lot of configuration changes (which is very tedious because each change in a policy set or client policy set binding requires a appserver restart), I finally found the cause: in the encrypter configuration I assumed "Data encryption" was the correct setting for encrypting the data of the message, but the infocenter clearly states that "Data encryption" is used for symmetric encryption only.


Changing the setting to "Key encryption" solved the InvalidKeyException!

Information center info about the setting:

Usage of key information reference
This field is available on this panel if you are configuring encryption protection and it specifies that the encryption key information is either data encryption key information or key encryption key information. Select Data encryption for symmetric algorithms and Key encryption for asymmetric algorithms.
 

Wednesday, November 2, 2011

Propagate user identity in JAX-WS web service on WebSphere using WS-Security

 

Introduction


This article will demonstrate how the user identity (the subject's user principle in java security) of the client can be used for authentication and authorization in a remote web service.

WS-Security is an extension to SOAP and can be used for integrity, confidentiality and authentication, I will focus on the authentication part here, and how the user identity can also be used for authorization in the web service after authentication.

The WS-Security policies are attached to a JAX-WS client or provider after deployment. The developer of the web service does not need to know or be bothered how the authentication is done at runtime. The developer only needs to specify which roles (he defined) can access which pieces of code.

Building a sample


Let's get started with a sample straight away. The RAD 8 project interchange file ("archive", as it is called now) is available at the end of the article.

In RAD 8 we create a new dynamic web project named "MySecureWebService" with all default settings:


We create a new class MyWebServiceImpl in the package arendatwork.wssecurity:


The only method in MyWebServiceImpl is a classic hello world implementation:

public String helloWorld(String name){
    System.out.println("MyWebService says hello " + name);
    return "hello " + name;
}

If we right-click the dynamic web project, we can choose new > other, we can type web service to launch the web service wizard:



The MyWebServiceImpl is the java bean implementation for our bottom up creation.   Be sure to select JAX-WS as the implementation type (in this case my default runtime is WAS 8 and jax-ws is selected by default. If your runtime is WAS7, JAX-RPC is still the default selection).  RAD will publish the web service for you straight away.

Let's create a client!



We will also generate jsp's for the client, therefore we will first create a new dynamic web project to hold these files. Completely similar to creating the first dynamic web project for the web service, we create one for the client, except for the names of the web project (MySecureWebServiceClient) and the enterprise project (MySecureWebServiceClientEAR).
Right click the web service in the enterprise explorer and navigate to generate > client

Slide the slider all the way to the top, up to "Test client", we want those generated jsp files. Also pick the MySecureWebServiceClient project as client project. That way we keep it separate from the project with the web service. Click finish.

If the appserver was already started, RAD will deploy the project. If not, a prompt is shown to start the server. As soon as your test server is running the wizard can finish the deployment of the test client. A browser with the generated test client will open automatically. If not, point your browser to the TestClient.jsp (in my case http://localhost:9080/MySecureWebServiceClient/sampleMyWebServiceImplPortProxy/TestClient.jsp ) If the endpoint is not specified in the test jsp, update it before testing.


On my machine, the endpoint is http://localhost:9080/MySecureWebService/MyWebServiceImplService This can vary depending on your port (server config), context root (project config) and web service name. The input of Arend will result in "hello Arend", while the sysout reads "MyWebService says hello Arend". Perfect!

Adding security



Now we can place a ws-security layer on top of our service. We will specify a policy set that requires an LTPA token as authentication. LTPA tokens are a WebSphere / IBM single sign on mechanism. ( wikipedia link ) The sample policy set bindings of WebSphere already specify how to generate and consume LTPA tokens, so we can reuse those bindings for this article. Log in to the admin console of your WAS environment, and navigate to Services > Policy sets > Application policy sets and click new :


Give "LTPA Only" as name, and add ws-security as policy:
Click on the ws-security policy we just added. Click main policy. In the main policy screen, unmark "Message level protection" and click apply:


then click "request token policies". Here you can add the tokens that are required for the policy set. Let's add LTPA, and give it a name (for example "LTPAToken):


The policy set bindings define how the policy set should be executed at runtime. If the policy set requires an LTPA token in the header, the client binding should define how an LTPA token is generated, and the provider binding knows how to interpret the token. WebSphere 8 has some policy set bindings packaged for both clients and providers. If you navigate in the admin console to Service > Policy sets > General provider policy set bindings you can see the list:


We only require standard WebSphere LTPA token in our ws-security policy, so we can use the "Provider sample" binding for our provider, and the "Client sample" for the client, because these bindings specify how to handle the LTPA tokens (and more). To attach the policy set we created and the sample binding to the provider, navigate to Services > Service providers, and click on our service "MyWebServiceImplService". As you can see you can attach the policy set + binding on different levels - service, port, or method. I'll stick the LTPA Only policy set to the service here, together with de default binding, Sample provider.

 
Save the configuration. After a new policy set has been created, the server has to be restarted to make it usable, so do that before testing.
Important: if you are deploying your applications directly from your eclipse workspace, make sure the publishing settings for your WAS server is set to "Run server with resources on Server". If the "Run server with resources within the workspace" setting is enabled, the policy set bindings do not work! You can attach them to the providers and clients, you can save it, all looks right, but no policy is active at runtime...

Let's run our test client again. You will most likely get a message like this:
CWWSS5514E: An exception while processing WS-Security message: com.ibm.wsspi.wssecurity.core.SoapSecurityException: CWWSS6521E: The Login failed because of an exception: javax.security.auth.login.LoginException: Login Failure: all modules ignored

What happened? Well, we didn't log in to our test client, so no LTPA token is generated! We can quickly setup basic authentication for our test client, so that a user principle is known when the soap message is created.

A requirement for security in your web application, is that global security is enabled in the WAS appserver AND application security is enabled. By right-clicking on the security roles section of the web deployment descriptor, we can add a role "authenticated".



After opening the web deployment descriptor, we can also add security constraints to /*. Only our role "authenticated" can access /*.


It is convenient to map the role "authenticated" directly to "all authenticated users" in the ear file. Right click on the MySecureWebServiceClientEar project, select JAVA EE > generate websphere bindings deployment descriptor. In the META-INF folder of the ear file, ibm-application-bnd.xml is generated. Add the security role "authenticated" and map it to the special subject "all authenticated users":




Now redeploy the MySecureWebServiceClientEar. If it goes right, your browser will prompt for a username and password when you access the test client page. When we hit the web service, we get our response as expected. Authentication is in place!

By adding
   
@Resource
    WebServiceContext context;

to the web service implementation, we can have access to the context, which will hold the userprinciple.
(I also needed to add the @WebService annotation to the class, otherwise the @Resource did not get injected, and context was null)
@WebService
  public class MyWebServiceImpl

We will add this line to our implementation:
System.out.println("MyWebService was called by " + context.getUserPrincipal());

If we login to the test client page with wasadmin, we expect the line "MyWebService was called by wasadmin". After all, we authenticated with wasadmin, right? However, it prints "MyWebService was called by null".

The Caller



The reason why the principal is null, is because we did not specify the Caller in the provider binding. This caller entry tells the appserver which
token should be used for the java security in the application itself. By setting the ltpa token our caller, we can propagate the user identity.
To set the caller, goto Services > Policy sets > General provider policy set bindings , and click on the "Provider sample" binding.
Remember that we used this default policy set binding for our sample. Click on the policy WS-Security, and click Caller:


Click new to create a new caller
Choose a name for your caller (for example "LTPA", it can be anything you like).
For "Caller identity local part" give the value "LTPAv2" and for "Caller identity namespace URI" the value "http://www.ibm.com/websphere/appserver/tokentype".
These are not chosen randomly. They specify which part of the header contains the identity information.



The possible values for the tokentypes out of the box defined in websphere application server, are listed in the infocenter:
http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/uwbs_wsspsbcald.html

It is possible to define multiple Callers, because it is possible to send different types of tokens. The order of the callers is the order of priority.
As soon as a token in the list of callers is found, that one is used for the identity.


The server should be restarted before we can test the sample, because the policy binding has changed.

"MyWebService was called by wasadmin"

It should work now; the userprinciple can be checked for a certain role.


Sample files


The sample files are available here


Tuesday, June 21, 2011

Top down web service and faults in RAD 8

If you create a topdown web service in RAD 8, using a WSDL you created in RAD with the WSDL wizard and the WSDL editor, java exceptions are not created or generated for the web service faults you added.

The reason for this is that the editor does not add a wsdl:fault to the binding section, when you add a fault to the portType. When you add the wsdl fault manually, the Java Exception will be generated.

I will demonstrate in detail what happens:

In RAD, create a new web project. Create a new WSDL file using the wizard in your project.


With the following options


A WSDL file with skeleton is created. When you open this file using the WSDL editor, it will look like this:


You can right click the operation name to add a fault:


NewOperation now has a fault:


Now we will create a java topdown web service using this wsdl. Right click your WSDL file and choose Generate Java Bean Skeleton:


The Web Service wizard will appear, no special options are selected:


We finish the wizard, and our Java implementation is generated. What do we see? The generated Java implementation has no Exception in its signature!


Now what is missing? The WSDL editor did not add the WSDL fault to the binding. You have to put that in place manually to make the exceptions work.


When we generate the Java Bean Skeleton again, the Exception is present in the signature

Wednesday, July 15, 2009

Logging SOAP messages AXIS client

If you want to debug your axis web service client and do not want to alter any code, you can follow this procedure to log all incoming and outgoing soap messages.

make a file with the name client-config.wsdd with this content:

<!--
Save this file as "client-config.wsdd" in the working directory
of your Axis client. Axis will load it automatically. The
configuration here tells Axis to save all incoming and outgoing
XML into a file named "axis.log"
-->

<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

<handler name="log" type="java:org.apache.axis.handlers.LogHandler" >
<parameter name="LogHandler.fileName" value="c:/logs/soapmessages.log"/>
</handler>

<globalConfiguration>
<requestFlow>
<handler type="log" />
</requestFlow>
<responseFlow>
<handler type="log" />
</responseFlow>
</globalConfiguration>

<transport name="http"
pivot="java:org.apache.axis.transport.http.HTTPSender" />

</deployment>

Dump it in the class root of the application, so the axis loader can pick it up (where you would place log4j.properties for example).

The only downfall is that you cannot dynamically change your logging level. You'll have to remove the file to disable the tracing.