Showing posts with label Web Service. Show all posts
Showing posts with label Web Service. Show all posts

Thursday, May 30, 2013

Retrieve and pass around http Authorization header with OSB

It never ceases to amaze me how i stumble on some "new" features with OSB. This time it's to retrieve and pass around http Authorization header with OSB.

I thought it was a long closed chapter that OSB strips off the http basic authentication header before the pipeline is invoked. Well, it's still true, except a colleague showed me there is a trick to make OSB show the http Authorization header!

The trick is to add -Dcom.bea.wli.sb.transports.http.GetHttpAuthorizationHeaderAllowed=true to the OSB start up command. Once you do that, (you still need to check "Get All Headers" on the proxy) you can access the authentication header like regular http user header.
   $inbound/ctx:transport/ctx:request/tp:headers/tp:user-header/@value
The above expression worked for me, because i have only one user-header. i would assume you need to do something like below if you have more than one user-header.
   $inbound/ctx:transport/ctx:request/tp:headers/tp:user-header[@name='Authorization']/@value

In my test case, i'm doing a bit more than just access it on the incoming proxy. I have 3 parts, Proxy1, a biz svc, and Proxy2. I want to pass the authorization header from proxy1 to proxy2 via the biz svc.

The screen below shows Proxy1 uses route to call the biz service, you need to set transport headers for "Outbound Request", check "Pass all Headers through pipeline". That should pass the authorization header on. In my case, i also tested with an additional custom header "aTestHeader" as shown in the screen shot.
One important note, on the biz service, normally it would require enabling "basic" authentication with a "Pass through" service account. However, if you added that magic start up option, then it doesn't matter what the biz service setting is. Because you are passing the authorization header explicitly. Additionally, if you want to see the authorization header in proxy2, you still need to check "pass all headers through pipeline".

On the biz service, that "pass through" service account will help you, if you don't use the magic start up option. In that case, the authorization header will not be explicitly accessible in the proxy, but the biz service will pass it on behind the scene. So if Proxy2 has enabled basic authentication, then the authorization header will be handed over to Proxy2 (behind the scene), so the authentication on proxy2 would succeed.

Wednesday, May 22, 2013

Java Web Service Resource Injection Fails on JMS Connection Factory When JMS Server Migrates

Here is the problem:
We have a clustered Weblogic environment. Let's say two managed servers M1 and M2. We have JMS server that needs be migrated among the managed servers. The JMS server is initially targeted to M1.

We have web services running on the cluster. One service publishes messages to the JMS server. This web service uses Java resource injection for the JMS connection factory like:
    @Resource(mappedName="ll.jmsprovider.XAConnectionFactory")
    private ConnectionFactory connectionFactory;

When the service is deployed to the cluster. "connectionFactory" is properly initialized, everything is fine. Even when we shutdown M1, the JMS resource is migrated to M2, the "connectionFactory" still works fine on M2. We can still publish messages.

The problem is when we bring back up M1, and shutdown M2. That's when "connectionFactory" doesn't work anymore. I suspect there might be some configurations on migration policy to make "connectionFactory" work seamlessly when the migration happens. But I don't know how. So my kludge is to recreate the "connectionFactory" on the fly. Here is the entire "hack":

package pubmsg;
import javax.annotation.Resource;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.SystemException;

@WebService(targetNamespace = "http://pubmsg.org/")
public class msgSender {
    @Resource(mappedName="ll.jmsprovider.XAConnectionFactory")
    private ConnectionFactory connectionFactory;
    @Resource(mappedName="ll.jms.partner.enroll.queue")
    private javax.jms.Queue queue;
      
    public msgSender() {
        super();
    }

    @WebMethod
    @Oneway
    public void sendMessage(String msg) {
        Connection conn = null;
        try {
            try {
                 conn = connectionFactory.createConnection(); // my "hack"
            }
            catch (Exception e) {
                InitialContext ctx = new InitialContext();      
                connectionFactory = (ConnectionFactory) ctx.lookup("ll.jmsprovider.XAConnectionFactory");
                conn = connectionFactory.createConnection();
                queue = (Queue) ctx.lookup("ll.jms.partner.enroll.queue");
            }
   Session session = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
   TextMessage message = session.createTextMessage(msg);
   MessageProducer messageProducer = session.createProducer(queue);
   messageProducer.send(message);
        }
        catch (NamingException e) {}
        catch (JMSException e) {}
        finally {
                if (conn!=null) {
                        try {
                                conn.close();
                        } catch (JMSException e) {}
                }
        }
    }
}

Not the most elegant solution. Oh, well, i need to move on...

Ironically, the purpose of this blog is keep track what i did and how i did it. But it has been several weeks since I worked on it, I already forgot the steps how I created the project :( So I just clicked through JDeveloper try to re-construct the same thing. I believe this is a sequence that can create a java based web service in Jdeveloper (11.1.1.6):

Part I - Ceate a "Web Project"
 1. select project name
 2. select default "serverlet 2.5..."
 3. chose "None" on the "page flow technology" screen
 4. chose no libraries.
 5. select doc root, app name, context root etc.
 6. finish

Part II - add a Java class,
    add the public methods as you need

Part III - add "Web Services->Java Web Service"
   select your java class, from that point on, pretty much follow the screen. That should just work.

====
hmm, i did find another way to create the Java web service: you can start with a generic application, then select "Web Services" for the default project, then "java" is selected by default. However, this process only works for the 1st default project. Once the application is created, and you try to add new project, and you pick "web services", you'll get different options, talking about confused...

Friday, May 10, 2013

Eclipse creates Java Web service client with WSS name token security

This is a combo task. It illustrates an end to end scenario of creating a Web service client based on a WSDL. Then it shows a way to add WSS name token security header, which ironically is different from the previous post http://yuanmengblog.blogspot.com/2013/05/jax-ws-client-call-web-services-with-ws.html

First of all, I use the same fooPS proxy service from OSB (from the previous post). You can find the wsdl from the previous post. I use Eclipse 3.7.1. Here are the steps:

Select JavaEE perspective. Create a new Web Service Client project: File->Other->Web Service Client.

On the next screen, enter your WSDL URL, then move the slide on the left side all the way up.

Then just click finish and let Eclipse does its magic.

When all said and done, Eclipse generates these files:
  • FooBPEL.java - interface, extends Remote
  • FooBPELBindingStub.java - implments FooBPEL and extends Stub
  • FooBPELProxy.java - implements FooBPEL
  • Foobpel_client_ep.java - interface implements Service
  • Foobpel_client_epLocator.java - extends Foobpel_client_ep
I added a server in Eclipse to point to my local Weblogic.


To deploy the new project to the server (I suppose there are better ways to do it): Select Run, Run As, Run on Server, then select the ear to deploy. I only do this once. If I make changes later, i just right click on the server (Tab on the bottom), then select "Publish".

When the deployment is finished, you will get a link http://localhost:7001/WebServiceProject/sampleFooBPELProxy/TestClient.jsp (again, all of these are auto generated, i haven't done a thing yet).

As you can see there is one "process()" method. Try invoke it, it would fail, because there is no security header. For sanity check, you can go to OSB fooPS, remove the WSS security policy, then try to invoke "process()" from the test page again, it should work correctly. Otherwise, you got other problems you need to sort out first before worrying about the security. If it works, then you just succesfully created a Web service client with Eclipse.

Next, I'll show you how I managed to add the security header to the client. In fact, the change is very mimium, but it took me a while to figure it out.

Firstly, i went down a path that did not work out. In the previous post (command line java web service client), the client program relies on casting the "port" into "BindingProvider", then set the security header that way. I just couldn't make it work with the Eclipse generated classes. I couldn't find the equivalent of the "Port" class in this case. Although there is getPort() method in "Foobpel_client_epLocator" class, but I have no way to cast it into the BindingProvider.

Here is what finally made it work: go to FooBPELProxy.java, find "process()" method, make changes as below:
...
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import weblogic.wsee.security.unt.ClientUNTCredentialProvider;
import weblogic.xml.crypto.wss.provider.CredentialProvider;
import weblogic.xml.crypto.wss.WSSecurityContext;
import org.apache.axis.message.SOAPHeaderElement;
import javax.xml.soap.SOAPElement;
...

 public java.lang.String process(java.lang.String input)
   throws java.rmi.RemoteException {
  if (fooBPEL == null)
   _initFooBPELProxy();

  org.apache.axis.client.Stub stub = null;
  try {
   stub = (org.apache.axis.client.Stub) fooBPEL;
   String wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
   String tstr = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
   SOAPHeaderElement security = new SOAPHeaderElement(wsse, "Security");
   SOAPElement usernameToken = security.addChildElement("UsernameToken");
   SOAPElement username = usernameToken.addChildElement("Username");
   username.addTextNode("weblogic");
   SOAPElement pwd = usernameToken.addChildElement("Password");
   pwd.addTextNode("welcome1");
   pwd.setAttribute("Type", tstr);
   stub.setHeader(security);
  } catch (Exception e) {
   System.out.println("exception=" + e.getMessage());
  }
  return fooBPEL.process(input);
 }

That's my hack to make it work. This may not be the way you code for production, it's just a proof of concept. 

JAX WS client call web services with WS-security name token - java command line version

I have an OSB proxy service running on http://localhost:8011/OWSM_Demo/fooPS?wsdl with WS name token security policy enabled. I need to call the service with the proper security header from Java.There are two scenarios: 1. call from command line Java app 2. call from Weblogic web app.

This post covers the command line verion. Web app version will be covered in the next post.

I followed this link: http://middlewaremagic.com/weblogic/?p=18. I first made the sample works as-is. I literrally followed the instructions with variations of my local environment. I have the SOA suite installed locally, i tested adminServer as well as the osb_server1 server. Here is what I did for osb_server1:
  1. In build.xml file, I just changed the weblogic/password, then changed following line for the server:<property name="wls.server.name" value="osb_server1"/> and http://localhost:8011/SecureHelloWorldImpl/SecureHelloWorldService?WSDL
  2. I ran setDomainEnv.cmd under C:\Oracle\Middleware\user_projects\domains\soa_domain\bin
  3. Then I just ran "ant" (the default target is "all").
The default target "all" compiles and deploys a sample web service to the weblogic server (AdminServer or osb_server1). Then generates the client stubs and finally compiles client code and runs the client.

The default build process first creates a web service with WSS name token security policy enabled. Then it generates the client classes based on the WSDL (very import point). Finally, it just invokes the service.This is all good.

Now I need to adapt the same sample to invoke my own OSB fooPS. Keep in mind, if you are only interested in creating a client that can call the OSB proxy fooPS, you only need do "ant client run" (skipping the server tasks).

If you read the build.xml file carefully, you'll see the "client" target is essentially independent of the actual sever implementation. It mostly generates the client classes based on the WSDL. From that I assume I can merely change the WSDL URL and make it work for my own OSB proxy service. Here is how I did it:

I changed WSDL URL in build.xml to point my service http://localhost1:8011/OWSM_Demo/fooPS?wsdl (see end of this post), then I ran "ant client" (I manully cleaned out the "webservicesSecurity_client" directory, you can use ant "clean" task to do it).

Of course, the build would fail because SecureHelloWorldClient.java can't find the original java classes from the original sample. But it did generate the client classes based on the new OSB WSDL, they are:
  • FooBPEL.java - interface
  • FooBPELAsyncHandler.java - I didn't do anything with this one
  • FoobpelClientEp.java - this one extends Service
  • ObjectFactory.java - didn't do anything
  • package-info.java - didn't do anything
  • Process.java - this is based on the only "operation" from the wsdl
  • ProcessResponse.java - operation response based on the WSDL
Now, change SecureHelloWorldClient.java from
  • SecureHelloWorldService service=new SecureHelloWorldService();
  • SecureHelloWorldPortType port=service.getSecureHelloWorldPortTypePort();
 to
  •  FoobpelClientEp service=new FoobpelClientEp();
  •  FooBPEL port=service.getFooBPELPt();
There you go. You can just type in "ant client run". It will invoke your OSB fooPS proxy! If you run into problems, try use TPCMon to debug the issue.

Of couse, you need to change your user/pass accordingly. Also, I didn't bother to change the client package name etc. This is just a proof of concept thing.

The meat part of the client program is to use BindingPort to set the WSS security header.

With the command line success under my belt, i started to migrate the solution to Web app. Of couse, it would be too easy it simply worked. And that's going to be covered in another post (http://yuanmengblog.blogspot.com/2013/05/eclise-creates-java-web-service-client.html).

I can't seem to find a way to add attachment, so here is the wsdl along with the xsd.

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="fooBPEL" targetNamespace="http://xmlns.oracle.com/cis/foo/fooBPEL" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:plnk="http://docs.oasis-open.org/wsbpel/2.0/plnktype" xmlns:client="http://xmlns.oracle.com/cis/foo/fooBPEL" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:WL5G3N0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
    <plnk:partnerLinkType name="fooBPEL">
        <plnk:role name="fooBPELProvider" portType="client:fooBPEL"/>
    </plnk:partnerLinkType>
<wsp:Policy wsu:Id="wss_username_token_service_policy" xmlns="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<sp:SupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10/>
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SupportingTokens>
</wsp:Policy>
    <wsdl:types>
        <schema xmlns="http://www.w3.org/2001/XMLSchema">
            <import namespace="http://xmlns.oracle.com/cis/foo/fooBPEL" schemaLocation="http://localhost:8011/OWSM_Demo/fooPS?SCHEMA%2FOWSM+Demo%2FfooBPEL"/>
        </schema>
    </wsdl:types>
    <wsdl:message name="fooBPELRequestMessage">
        <wsdl:part name="payload" element="client:process"/>
    </wsdl:message>
    <wsdl:message name="fooBPELResponseMessage">
        <wsdl:part name="payload" element="client:processResponse"/>
    </wsdl:message>
    <wsdl:portType name="fooBPEL">
        <wsdl:operation name="process">
            <wsdl:input message="client:fooBPELRequestMessage"/>
            <wsdl:output message="client:fooBPELResponseMessage"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="fooBPELBinding" type="client:fooBPEL">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
      <wsp:PolicyReference URI="#wss_username_token_service_policy" wsdl:required="false"/>
        <wsdl:operation name="process">
            <soap:operation style="document" soapAction="process"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="foobpel_client_ep">
        <wsdl:port name="fooBPEL_pt" binding="client:fooBPELBinding">
            <soap:address location="http://thinkpad01:8011/OWSM_Demo/fooPS"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

<?xml version="1.0" encoding="UTF-8"?>
<schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://xmlns.oracle.com/cis/foo/fooBPEL" xmlns="http://www.w3.org/2001/XMLSchema">
 <element name="process">
  <complexType>
   <sequence>
    <element name="input" type="string"/>
   </sequence>
  </complexType>
 </element>
 <element name="processResponse">
  <complexType>
   <sequence>
    <element name="result" type="string"/>
   </sequence>
  </complexType>
 </element>
</schema>