Archives

Archives / 2006 / March
  • WS-Transfer - Are We Talking HTTP Over XML Over HTTP?

    I may be daft, but after reading the comments on WS-Transfer submission to the W3C, I get the feeling that WS-Transfer feels... I dunno, wrong? I know SOAP is transport independant, but a vast majority of the SOAP calls are made over HTTP, so are we talking HTTP over XML/SOAP over HTTP?

    WS-Transfer is about doing resource manipulation, using HTTP-like verbs such as GET, PUT, DELETE and such. Feels like a more complex REST style of managing resources to me.

  • Building Web Services the REST Way

    There is much talk at the moment about REST, what is it and not is, Lo-REST, Hi-REST and whatnot. For those of you who don't really know what REST is and don't have time (or like me, not the brain) to delve into Fielding's full REST description, Roger L. Costollo wrote a short piece called Building Web Services the REST Way, which I think is pretty good. Some of you will recognize the REST style from XML on HTTP solutions you did 5-6 years ago, before the dawn of the SOAP and ROPE...

    It seems to me that some people think Microsoft is not supporting HTTP/XML/REST the way it should. If you want to get updated on the matter, go to Don's blog and Jonnay's blog (Sacrificial Rabbit) and start to read. 

  • Factory Factory Factory...

    From last year, but so true and very funny - BenjiSmith wrote a story around why he hates frameworks.

    I'm currently in the planning stages of building a hosted Java web application (yes, it has to be Java, for a variety of reasons that I don't feel like going into right now). In the process, I'm evaluating a bunch of J2EE portlet-enabled JSR-compliant MVC role-based CMS web service application container frameworks.

    And after spending dozens of hours reading through feature lists and documentation, I'm ready to gouge out my eyes.

    I know exactly what he means :D

     

  • RSS Toolkit Update

    MSDN blogger Dmitryr made a minor update to his excellent RSS toolkit:

    I am posting a minor update to the ASP.NET RSS Toolkit (version 1.0.0.1).  It contains changes made in response to the feedback I received -- I'd like to thank everyone for their comments and suggestions!

    Check it out.

  • [Vista] Vista VPC File Size

    If someone's interested - the size of the virtual hard drive after installing Vista + VMAdditions is around 8 GIG. I could try to shrink the file size, but I guess it wouldn't help much. Seems pretty hard to copy it to a 4.7 GB DVD then...
  • [Vista] Testing Vista Feb CTP on VirtualPC

    Today I installed the Feb CTP of Vista on my box at home, under VPC. The install was noticably faster than before, but even so took about an hour I think. I went out for a while, enjoying the spring sun, so I'm not sure exactly how long the install took.

    After reading some advice on the Net, I installed the VMAdditions from the Virtual Server R2 program instead of the additions included in VPC. I don't know much of the difference, but performance in Vista is good enough to give it a real go. Just missing the Glass effects and sound is not working, but these are known issues.

    The only thorn in my side right now is that I cannot get networking to work. I've seen some problems related to NAT and such, but I have to read up on it to see if it is related to my problems.

  • [Vista] Virtual Vista Bloggers

    An excellent way to test out a new operating system like Vista is to run it in a virtual machine, but some people run into problems installing and getting good performance out of it. The last days I've been reading posts from these two guys on the topic - Mikekol runs a blog called "Virtual Vista" which has several good posts about Vista on VPC and Virtual PC Guy blogs about... yes you guessed it!

  • [Java] Testing SOAP Headers with a Simple Axis Handler

    This is not a tutorial, but just some sharing of my brief learnings when testing a simple Axis Handler (somewhat similar to ASP.NET Soap Extensions). I wrote 2 programs - one simple web service client as a Java console program and one web service. The client calls a web service method called test() but adds a simple SOAP header containing a username and a password. The web service has a BasicHandler, defined in the deployment descriptor as a request handler, which checks each request and makes sure it contains the correct SOAP header with the correct username and password. The handler also sets the validated username in a ThreadLocal variable, which is then picked up by the web service method. If the request does not contain the correct SOAP header, username and password, a fault is thrown.

    There are several ways for the handler class to set data which can be picked up by the web service method, and I know there is a debate going on around the use of ThreadLocals, but I thought it could be useful in this particular case.

    I don't think it matters much which version of Axis you use, but I went for Axis version 1.3. Note that this is test-code, not tested for production. If you find something smelly in the code, I would love to hear about it. Another note is the way I build up the SOAP header and my somewhat sloppy usage of XML namespace. I know it can be done much better, but the intention was to test the handler code :)

    Note that the web service client code is auto-generated from the WSDL and I've not bothered to show the code for it here.

    First, the client code:

    package test;

    import org.apache.axis.message.*;

    public class MyClient {

     public static void main(String[] args) {
     
      System.out.println("Hello, this is the client!");
     
      MyServiceServiceLocator wsloc = new MyServiceServiceLocator();
     
      SOAPHeaderElement oHeaderElement;
      javax.xml.soap.SOAPElement oElement;
      MyServiceSoapBindingStub ws;
     
       try {
        ws = (MyServiceSoapBindingStub)wsloc.getMyService();

        oHeaderElement = new SOAPHeaderElement("http://test",
         "securityHeader");
        oHeaderElement.setPrefix("sec");
        oHeaderElement.setMustUnderstand(false);
        oElement = oHeaderElement.addChildElement("username");
        oElement.addTextNode("johan");
        oElement = oHeaderElement.addChildElement("password");
        oElement.addTextNode("secret");
         
        ws.setHeader(oHeaderElement);
        System.out.println(ws.test());
       } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
       }
     
     }

    }


    Then, the web service code (most comments and javadoc removed):

    package test.service;

    import test.service.handler.MyHandler;

    public class MyService
    {
     public String test()
     {
      String username = MyHandler.getUsername();
      MyHandler.setUsername(null); //clean up
     
      return "Hello from Service! User is = " + username;
     }
    }

    Finally, the Handler code looks something like this:

    package test.service.handler;

    import org.apache.axis.AxisFault;
    import org.apache.axis.Message;
    import org.apache.axis.MessageContext;
    import org.apache.axis.handlers.BasicHandler;

    import java.util.Iterator;

    import javax.xml.soap.SOAPException;
    import javax.xml.soap.SOAPHeader;
    import javax.xml.soap.SOAPHeaderElement;

    import org.apache.axis.message.SOAPEnvelope;

    /**
     * Simple Axis handler which looks for a SOAP header
     * containing two elements - username and password. Invoke() method
     * checks these...
     *
     * @author Johan Danforth
     */
    public class MyHandler extends BasicHandler {

     private static final long serialVersionUID = 1L;
     private static ThreadLocal _username = new ThreadLocal();

        public static String getUsername() {
            return ((String) (_username.get())).toString();
        }
       
        public static void setUsername(String value) {
            _username.set(value);
        }
     
     /**
      * Method called by Axis handler, checks SOAP header with
      * username and password.
      *
      * @throws AxisFault if header is missing or invalid or wrong username or password
      */
     public void invoke(MessageContext msgContext) throws AxisFault {

      boolean processedHeader = false;
     
      try {
       Message msg = msgContext.getRequestMessage();
       SOAPEnvelope envelope = msg.getSOAPEnvelope();
       SOAPHeader header = envelope.getHeader();
       Iterator it = header.examineAllHeaderElements();
       SOAPHeaderElement hel;
      
       while (it.hasNext()) {
        hel = (SOAPHeaderElement) it.next();
        String headerName = hel.getNodeName();
        if(headerName.equals("sec:securityHeader"))
        {
         checkUsername(hel);
         processedHeader = true;
        }
       }
      } catch (SOAPException e) {
       //capture and wrap any exception.
       throw new AxisFault("Failed to retrieve the SOAP Header or it's details properly.", e);
      }

      if(!processedHeader)
       throw new AxisFault("Failed to retrieve the SOAP Header");
     
     }

     private void checkUsername(SOAPHeaderElement hel) throws AxisFault {
      String username = getUsername(hel);
      String password = getPassword(hel);
     
      if(!(username.equals("johan") && password.equals("secret")))
      {
       throw new AxisFault("Access Denied");
      }
      else
      {
       //set username as threadlocal variable
       _username.set(username);
      }
     }

     private String getPassword(SOAPHeaderElement hel) throws AxisFault {
      org.w3c.dom.Node passwordNode = hel.getLastChild();
      String nodename = passwordNode.getNodeName();
      if(!nodename.equals("sec:password"))
       throw new AxisFault("Missing password element in SOAP header.");
      String password = passwordNode.getFirstChild().getNodeValue();
      System.out.println("password = " + password);
      return password;
     }

     private String getUsername(SOAPHeaderElement hel) throws AxisFault {
      org.w3c.dom.Node usernameNode = hel.getFirstChild();
      String nodename = usernameNode.getNodeName();
      if(!nodename.equals("sec:username"))
       throw new AxisFault("Missing username element in SOAP header.");
      String username = usernameNode.getFirstChild().getNodeValue();
      System.out.println("username = " + username);
      return username;
     }
    }


    I need to share with you the deployment descriptors and the web.xml as well. I run the Axis Servlets in the same web app as my web service code. It's up to you how you prefer to do it really. I thought it was easier this way when I used the latest version of Eclipse. That's why the web.xml is full of Axis stuff and I'm using a separate server-config.wsdd instead of registering the service through the Axis admin tool pointing at a separate deploy.wsdd file. I'm attaching a sample deploy.wsdd file as well, just in case someone is unfamiliar with the server-config file.

    My web.xml looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.4" xmlns="
    http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
     <display-name>
     HelloWorld2</display-name>
     <servlet>
      <display-name>
      Apache-Axis Servlet</display-name>
      <servlet-name>AxisServlet</servlet-name>
      <servlet-class>
      org.apache.axis.transport.http.AxisServlet</servlet-class>
     </servlet>
     <servlet>
      <display-name>
      Axis Admin Servlet</display-name>
      <servlet-name>AdminServlet</servlet-name>
      <servlet-class>
      org.apache.axis.transport.http.AdminServlet</servlet-class>
      <load-on-startup>100</load-on-startup>
     </servlet>
     <servlet-mapping>
      <servlet-name>AxisServlet</servlet-name>
      <url-pattern>/servlet/AxisServlet</url-pattern>
     </servlet-mapping>
     <servlet-mapping>
      <servlet-name>AxisServlet</servlet-name>
      <url-pattern>*.jws</url-pattern>
     </servlet-mapping>
     <servlet-mapping>
      <servlet-name>AxisServlet</servlet-name>
      <url-pattern>/services/*</url-pattern>
     </servlet-mapping>
     <servlet-mapping>
      <servlet-name>AdminServlet</servlet-name>
      <url-pattern>/servlet/AdminServlet</url-pattern>
     </servlet-mapping>
     <welcome-file-list>
      <welcome-file>index.html</welcome-file>
      <welcome-file>index.htm</welcome-file>
      <welcome-file>index.jsp</welcome-file>
      <welcome-file>default.html</welcome-file>
      <welcome-file>default.htm</welcome-file>
      <welcome-file>default.jsp</welcome-file>
     </welcome-file-list>
    </web-app>


    My server-config.wsdd looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <deployment xmlns="
    http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
     <globalConfiguration>
      <parameter name="sendMultiRefs" value="true"/>
      <parameter name="disablePrettyXML" value="true"/>
      <parameter name="adminPassword" value="admin"/>
      <parameter name="attachments.Directory" value="C:\Java\eclipse\workspace\HelloWorld2\.deployables\HelloWorld2\WEB-INF\attachments"/>
      <parameter name="dotNetSoapEncFix" value="true"/>
      <parameter name="enableNamespacePrefixOptimization" value="true"/>
      <parameter name="sendXMLDeclaration" value="true"/>
      <parameter name="sendXsiTypes" value="true"/>
      <parameter name="attachments.implementation" value="org.apache.axis.attachments.AttachmentsImpl"/>
      <requestFlow>
       <handler type="java:org.apache.axis.handlers.JWSHandler">
        <parameter name="scope" value="session"/>
       </handler>
       <handler type="java:org.apache.axis.handlers.JWSHandler">
        <parameter name="scope" value="request"/>
        <parameter name="extension" value=".jwr"/>
       </handler>
      </requestFlow>
     </globalConfiguration>
     <handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder"/>
     <handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/>
     <handler name="Authenticate" type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/>
     <service name="AdminService" provider="java:MSG">
      <parameter name="allowedMethods" value="AdminService"/>
      <parameter name="enableRemoteAdmin" value="true"/>
      <parameter name="className" value="org.apache.axis.utils.Admin"/>
      <namespace>http://xml.apache.org/axis/wsdd/</namespace>
     </service>
     <service name="Version" provider="java:RPC">
      <parameter name="allowedMethods" value="getVersion"/>
      <parameter name="className" value="org.apache.axis.Version"/>
     </service>
     <service name="MyService" provider="java:RPC" style="wrapped" use="literal">
      <operation name="test" qname="ns3:test" returnQName="ns3:testReturn" returnType="xsd:string" soapAction="" xmlns:ns3="
    http://test" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
      <parameter name="allowedMethods" value="test"/>
      <requestFlow>
       <handler type="java:test.service.handler.MyHandler">
        </handler>
      </requestFlow>
      <parameter name="typeMappingVersion" value="1.2"/>
      <parameter name="wsdlPortType" value="MyService"/>
      <parameter name="className" value="test.service.MyService"/>
      <parameter name="wsdlServicePort" value="MyService"/>
      <parameter name="schemaQualified" value="
    http://test"/>
      <parameter name="wsdlTargetNamespace" value="
    http://test"/>
      <parameter name="wsdlServiceElement" value="MyServiceService"/>
     </service>
     <transport name="http">
      <requestFlow>
       <handler type="URLMapper"/>
       <handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/>
      </requestFlow>
      <parameter name="qs:list" value="org.apache.axis.transport.http.QSListHandler"/>
      <parameter name="qs:wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/>
      <parameter name="qs.list" value="org.apache.axis.transport.http.QSListHandler"/>
      <parameter name="qs.method" value="org.apache.axis.transport.http.QSMethodHandler"/>
      <parameter name="qs:method" value="org.apache.axis.transport.http.QSMethodHandler"/>
      <parameter name="qs.wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/>
     </transport>
     <transport name="local">
      <responseFlow>
       <handler type="LocalResponder"/>
      </responseFlow>
     </transport>
    </deployment>


    A sample deploy.wsdd could look like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Use this file to deploy some handlers/chains and services      -->
    <!-- Two ways to do this:                                           -->
    <!--   java org.apache.axis.client.AdminClient deploy.wsdd          -->
    <!--      after the axis server is running                          -->
    <!-- or                                                             -->
    <!--   java org.apache.axis.utils.Admin client|server deploy.wsdd   -->
    <!--      from the same directory that the Axis engine runs         -->
    <deployment xmlns="
    http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

      <service name="MyService" provider="java:RPC" style="wrapped" use="literal">
          <parameter name="wsdlTargetNamespace" value="
    http://test"/>
          <parameter name="wsdlServiceElement" value="MyServiceService"/>
          <parameter name="schemaQualified" value="
    http://test"/>
          <parameter name="wsdlServicePort" value="MyService"/>
          <parameter name="className" value="test.service.MyService"/>
          <parameter name="wsdlPortType" value="MyService"/>
          <parameter name="typeMappingVersion" value="1.2"/>
          <operation xmlns:operNS="
    http://test" xmlns:retNS="http://test" xmlns:rtns="http://www.w3.org/2001/XMLSchema" name="test" qname="operNS:test" returnQName="retNS:testReturn" returnType="rtns:string" soapAction="">
          </operation>
          <parameter name="allowedMethods" value="test"/>
      <requestFlow>
       <handler type="java:test.service.handler.MyHandler">
        </handler>
      </requestFlow>
      </service>
    </deployment>