28 January 2011

Writing a WSDL 1.1 Web Service Contract by Hand

A WSDL document may be confusing at first glance, but is a fairly simple XML document to code by hand once you've understood the concepts. This tutorial will help you writing and understanding the WSDL.

WSDL Layout
The WSDL essentially defines operations grouped as a service, the way these services are called (SOAP etc.) and where the service is hosted.

We will start by defining the service interface (what operations will be exposed) and the messages used by the operations, then will create the binding and finally the endpoint where the service is hosted.

The service create will be using the document/literal/wrapped style thats is most common for creating services that are interoperable.

Designing the Service
We first need to decide what the service will be: what operations it will be hosting:

  • Service Name: Users
  • Operations: 
    • addUser - takes user, no return
    • getUser - takes user id, returns user

Basic WSDL
The root element is 'definitions' and we will be declaring all the namespaces on the root element:

<?xml version="1.0"?> 
<wsdl:definitions name="Users" 
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
    xmlns:schema="http://www.example.com/ns/user" 
    xmlns:tns="http://www.example.com/ns/user/service" 
    targetNamespace="http://www.example.com/ns/user/service" 
> 

<wsdl:documentation> 
Example Service to demonstrate WSDL authoring. 
</wsdl:documentation> 

</wsdl:definitions>

Namespaces:
  • wsdl - the WSDL namespace
  • soap - the SOAP binding
  • schema - our example data schema
  • tns - our namepsace for the WSDL

Interface Design
Here we will define the service and its operations. We will do this by using the portType element.

We will call the portType UserEI where 'EI' denotes 'Endpoint Interface'. The getUsers operation is a standard request/response type call or IN-OUT Message Exchange Pattern (MEP), the addUser operation we will make an IN-ONLY MEP.

<wsdl:portType name="UserEI">
    <wsdl:documentation>User operations</wsdl:documentation>
   
    <wsdl:operation name="getUser">
        <wsdl:documentation>Get a User by User ID</wsdl:documentation>
        <wsdl:input message="tns:getUser"/>
        <wsdl:output message="tns:getUserResponse"/>
    </wsdl:operation>

    <wsdl:operation name="addUser">
        <wsdl:documentation>Add a User</wsdl:documentation>
        <wsdl:input message="tns:addUser"/>
    </wsdl:operation>

</wsdl:portType>

Note that the messages (tns:getUser etc..) are not yet defined, see the next section.

Messages
Now we define the messages using the 'message' element. The messages are mapped to elements defined in the external XSD.

<wsdl:message name="getUser">
    <wsdl:part element="schema:getUser" name="parameters"/>
</wsdl:message>

<wsdl:message name="getUserResponse">
    <wsdl:part element="schema:getUserResponse" name="parameters"/>
</wsdl:message>

<wsdl:message name="addUser">
    <wsdl:part element="schema:addUser" name="parameters"/>
</wsdl:message>

Note that the part refers to the 'element' defined in the XSD, not the 'type', the part name is 'parameters', the element name is the same as the related operation name and there is only one part per message. These are important rules in create 'wrapped' style services.

Message Schema
As the messages can only have one part/element, this element becomes a 'wrapper' for the data 'parameters' the message contains. I'm not going to go into detail on the XSD schema, as its not the focus of this tutorial. This is how the separate XSD file could look:

<?xml version="1.0"?>
<xsd:schema 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:msg="http://www.example.com/ns/user"
    targetNamespace="http://www.example.com/ns/user"
    elementFormDefault="qualified"
    attributeFormDefault="qualified"
>

<xsd:element name="getUser" type="msg:getUser"/>
<xsd:element name="getUserResponse" type="msg:getUserResponse"/>
<xsd:element name="addUser" type="msg:addUser"/>

<xsd:complexType name="getUser">
    <xsd:sequence>
        <xsd:element name="id" type="xsd:string"/>
    </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="getUserResponse">
    <xsd:sequence>
        <xsd:element name="user" type="msg:user"/>
    </xsd:sequence>
</xsd:complexType>
 
<xsd:complexType name="addUser">
    <xsd:sequence>
        <xsd:element name="user" type="msg:user"/>
    </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="user">
    <xsd:sequence>
        <xsd:element name="id" type="xsd:string"/>
        <xsd:element name="name" type="xsd:string"/>
    </xsd:sequence>
</xsd:complexType>
<xsd:schema>

Import the data definitions
The file is imported under the same namespace we declared at the beginning of this tutorial (http://www.example.com/ns/user).

<wsdl:types> 
  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <xsd:import namespace="http://www.example.com/ns/user" schemaLocation="userSchema.xsd"/> 
  </xsd:schema> 
</wsdl:types>

All that left now is to define the binding and where the service is hosted:

Binding
In this example we are going to define SOAP bindings for the service (most common binding). Note that we bind to the interface or 'portType' (tns:UserEI) we defined earlier.

We are using the 'document' style as opposed to 'RPC' and the SOAP body is 'literal' as opposed to 'encoded'.


<wsdl:binding name="UserBinding" type="tns:UserEI">
  <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>

  <wsdl:operation name="getUser">
    <soap:operation soapAction="http://www.example.com/ns/user/action/getUser"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
  </wsdl:operation>

  <wsdl:operation name="addUser">
    <soap:operation soapAction="http://www.example.com/ns/user/action/getUser"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
  </wsdl:operation>

</wsdl:binding> 

Endpoint
The last thing we need is to specify where the service is being hosted:

<wsdl:service name="UserService"> 
  <wsdl:port binding="tns:UserBinding" name="UserEndpoint"> 
    <soap:address location="http://www.example.com/services/User"/> 
  </wsdl:port> 
</wsdl:service>

Testing the WSDL
When writing something like this by hand, you will make mistakes: in writing the code above I made 4 mistakes, all relating to typos (element names not matching etc..). I checked the WSDL by running Java's wsimport tool, which takes the WSDL as input and generates Java artifacts. Here is the command I ran:

wsimport -keep -Xnocompile -s src users.wsdl

Which generates the Java source in the 'src' directory, does not compile it and keeps the source code.

You could use a tool like soapui, or an XML editor of your choice.




15 March 2010

Dumming Down the User Interface Again

One of my clients recently asked me to track changes they needed on their eCommerce website I've been maintaining over the last five years. Now, most of you will probably be going 'Hello? Brian has no issue tracking in place for his clients?', but let me defend myself: My client has a low tolerance for learning or being involved in anything that does not impact their business directly.

I know and understand the advantages of issue tracking and have used and installed a few over the years, almost all opens source (bugzilla, track, scarab et al) and I know the pain of working with any of these tools for the first time: its a big WTF with new terminology and lots of options. Its one of those areas where the domain is  simple, but everybody likes it slightly differently: how does an issue start out, is it 'open' is 'started' etc and is it 'high' priority, is it a bug, a feature etc..

What was ironic about the request is that I had set up issue tracking for them at least a year previously with a system I thought simple enough for them to just start using (they are completely tech literate), but they never used it (they did login though).

I went back to the old stand by: A shared Google Docs Spreadsheet: it is simple and it works and it is better than the 'spreadsheet in a shared folder' pattern, or the 'lets email the spreadsheet around' pattern. We've all been down there before and it is hell. The main problem with spreadsheets is maintaining them for any length of time. They work for month long projects, but anything longer can get chaotic.

I then spoke with a business friend of mine who is also using a spreadsheet to manage his workers and was complaining about it getting messy and out of state (it might have been a word document). I asked him why he was not using any tools to do this and he said he had tried, but that they were too limiting: he did not care who was updating the system and had no time for confirming tasks etc..

I was taken aback and forced to think about it, of course he is right. He should not have to adjust the way he works for a tool and the other thing that struck me was that what most of these tools try and enforce should be enforced by other processes rather. If you can't trust your workers to update and maintain a tracking system, you have bigger issues within your business.

Back to user-interfaces: We need to find some balance between the complex and the simple, between overwhelming the user with options and leaving him to do everything manually.

Mostly, application development is about automating things people don't want to do. No need complicate it any more than that.

The next irony in the story is that people love buying complexity! Using Gmail for the first time was a revelation: no complexity, simple interface - less stressful. The interface made me think about interface design in a different way: how can I remove options and how can I streamline the most important tasks.

19 January 2010

JEE6 with Netbeans 6.8 and Glassfish 3




I've been using JEE6 with Netbeans and Glassfish for a while and decided I should update my tutorial Creating a JEE application in Netbeans 6.5 to JEE6.


I'm following a similar loose format and creating a skeleton of an application to show case the various tiers (ORM, business objects and web) and give you an overview of what is possible.

I also want to demonstrate how easy developing EJB applications has become since the spec came out. I wouldn't be developing with it if it wasn't.

The musical background for today comes from The Willing Mind as a shameless plug ;). Find our music here: The Willing Mind on last.fm, The Willing Mind on Facebook

OK, I've got Netbeans 6.8 loaded, this is what I am running on:


Product Version: NetBeans IDE 6.8 (Build 200912041610)
Java: 1.6.0_16; Java HotSpot(TM) 64-Bit Server VM 14.2-b01
System: Linux version 2.6.28-17-generic running on amd64; UTF-8; en_ZA (nb)

I'm in my testing project group and turning the music on (Charls Riff).

15:00 - Create new Project

File -> New Project -> Java EE -> Enterprise Application -> 'Next'

Project Name: BooksDemo

'Next' again and keep the defaults (Glassfish v3 Domain, Java EE 6 etc)

'Finish'

Three projects will be created for you, the EJB project, the Web Project and the EE project references to EJB and Web project. In my case they were called BooksDemo, BooksDemo-ejb and BooksDemo-war

15:04 - Create the Datastore

The Database

In your 'Services' tag (Ctrl+5) under 'Databases' select Java DB, right click and 'Create Database':

Database Name: booksdemo
User Name: booksdemo
Password: booksdemo


'OK' and the new Java DB database will be created and start the server if not started.

Now the Persistance Unit

Back to your 'Projects' window (Ctrl+1), select the EJB project (BooksDemo-ejb), right click -> New -> Other -> Persistence -> Persistence Unit -> 'Next'

For the Data Source, in the drop down select 'New Data Source..'

JNDI Name: jdbc/booksdemo
Database Connection: <select the database you created earlier>


'OK'

Leave everything else as default and press 'Finish'.

The Entity class

In the projects window right click on the EJB project again and select 'New' -> 'Other' -> Persistence -> Entity Class -> 'Next'

Class Name: Book
Package: com.gamatam.tutorial.book.model

'Finish'

Now add a field to the Book class for the title:

Alt+Insert -> Add Property

Name: title

'OK'

Thats our data layer finished! (listening to 'Lick' by 'The Willing Mind' is it wrong to love your own music so much?)

15:23 - Create the Session Bean

In the projects window, select the EJB project and right click again -> New -> Other -> Java EE -> Session Bean -> 'Next'

EJB Name: BookService
Package: com.gamatam.tutorial.book.service

'Finish'

In the BookService class, right click -> Persistence -> User Entity Manager, this will add an instance variable for the entity manager.

Now we need the business methods, really easy: Alt+Insert -> Add Business Method

Name: getAll
Return Type: List<Book>

'OK'

Alt+Insert -> Add Business Method:


Name: add
(Parameters) 'Add'
Name: book
Type: Book


'OK'

Now to get rid of the squigly lines, or to fix the imports: Ctrl+Shift+i

Next we need to flesh out the methods a bit.


public List<Book> getAll() {
return em.createQuery("select b from Book b").getResultList();
}

public void add(Book book) {
em.persist(book);
}



OK, that was the business tier, please note there are no longer any interfaces created for the SessionBean!

15:36 - The Web

hmm, thinks a bit as to how to go forward this time..

Lets run the application here and do a sanity check: right click on the BooksDemo main project (probably highlighted) and select 'Run'.

32 seconds to build, browser coming up and we have 'Hello World!' staring at us, yay. We are looking at the default index.jsp page that was created by Netbeans when we created the project, there is no connection to the datastore or enterprise bean at this stage, but the database should have bean created.

Go to the Services window (Ctrl+5), under Databases select the database that we created at the begining of the tutorial, right click and 'Connect', then click to explore the node and select 'BOOKSDEMO', open it and open the 'Table' node.

You will see that the 'BOOK' table has been created for you and is made up of an 'ID' column and a 'TITLE' column to mach the properties in the Book entity class.

15:46 - Add Book Servlet

Back to the project window (Ctrl+1), select the war project (BooksDemo-war), right click -> New -> Other -> Web -> Servlet -> Next

Class Name: BookAdd
Package: com.gamatam.tutorial.book.web.action
'Next' and 'Finish'

Note the new 'Webservlet' attribute on the class, this means no more editing of the web.xml for serlvets!

Alt+Insert -> Call Enterprise Bean -> select the EJB project (BooksDemo-ejb) and select the BookService -> 'OK'

Remove everything from the 'processRequest' method and we will get the book name as a paramter, create a Book object and persist it using the SessionBean:

protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String title = request.getParameter("title");
Book book = new Book();
book.setTitle(title);
bookservice.add(book);
response.sendRedirect("index.jsp");
}



Once you save the servlet, Netbeans should deploy it, so you can test it by trying the following URL:

http://localhost:8080/BooksDemo-war/BookAdd?title=test

You will not see much as the servlet redirects back to the index page, but lets look in the database:

In the Services window (Ctrl+5), right click on the BOOK table and select 'View Data' - you will see a new row inserted with the title 'test'.

16:00 - Custom Tag

Now we will create a custom tag to view all the books:

Back to the project window (Ctrl+1) and right click on the war project -> New- > Other -> Web -> Tag Library Descriptor -> 'Next'


TLD Name: books


'Finish'

right click on the war project -> New- > Other -> Web -> Tag Handler -> 'Next'

Class Name: BookTag
Package: com.gamatam.tutorial.book.web.tag
'Next'
TLD File: WEB-INF/tlds/books.tld
Tag Name: book


'Finish'

Next we add a reference to the BookService EJB and modify the doTag method so that loops through all the books:

public class BookTag extends SimpleTagSupport {
@EJB
Bookservice bookservice;

@Override
public void doTag() throws JspException, IOException {
List<Book> books = bookservice.getAll();
for(Book book: books){
getJspContext().setAttribute("book", book);
getJspBody().invoke(null);
}
}
}



Next, in the index.jsp page we need to first declare the tag library and then use it:

Open the index.jsp page (war project, Web Pages, index.jsp) and add the following:

<%@taglib uri="/WEB-INF/tlds/books" prefix="b"%>

And in the page body:

<b:book>
${book.title}
</b:book>

If you reload the page in your browser, you will see a list of books added to the datastore.

And lastly, I will add a simple form for adding a book:

<form action="BookAdd"><input name="title"><input type="submit"></form>



And at 16:17, thats a wrap!


I'll leave it as an exercise for you to 'dolly' it up.

04 November 2009

JDBC Realm Setup with Glassfish v3 (build 70) and Netbeans 6.8 (beta) JEE6

As I am about to undertake this and I can't find instructions using the glassfish GUI admin, I might as well blog it:


Database
Create Database
I'm going to use the Derby/JavaDB database that came with Netbeans:

Click on the 'services' tab in Netbeans
Right click on JavaDB under Databases and select 'Create Database..'
Input details for the database, I used 'security' for database name , username and password (ironically).

Create Connection Pool for Database
Then in the Glassfish admin panel:
Resources->JDBC->Connection Pools, select 'New'
Name: security
Resource Type: javax.sql.DataSource
Database Vendor: JavaDB
<next>
Scroll down to additional properties:
DatabaseName: security
User: security
Password: security
ServerName: localhost

NB! Delete all other properties.

<save>
then try 'ping' it

make sure your database is up and running, compare properties to other loaded connection pools..

Create JDBC Resource
In the Glassfish admin panel:
Resources->JDBC->JDBC resources, select 'new'
JNDI Name: jdbc/security
Pool Name: security
<OK>

Create Tables
In Netbeans 'Services' tab:
Database->jdbc:derby://localhost:1527/security, right click and select 'Execute Command'

create table usertable (
        username varchar(128) NOT NULL CONSTRAINT USER_PK PRIMARY KEY ,
        password varchar(128) NOT NULL
);
and

create table grouptable(
        username varchar(128) NOT NULL,
        groupid  varchar(128) NOT NULL,
        CONSTRAINT GROUP_PK PRIMARY KEY(username, groupid),
        CONSTRAINT USER_FK FOREIGN KEY(username) REFERENCES usertable(username)
            ON DELETE CASCADE ON UPDATE RESTRICT
    );
and populate:

insert into usertable(username,password) values ('admin', '21232f297a57a5a743894a0e4a801fc3');
insert into grouptable(username,groupid) values ('admin', 'USER');
insert into grouptable(username,groupid) values ('admin', 'ADMIN');


scripts modified slightly from http://blogs.sun.com/foo/resource/createschema.sql from the article http://blogs.sun.com/foo/entry/mort_learns_jdbc_realm_authentication

Which is pretty much what I am doing..

Realm
Create New
In the Glassfish admin panel:
Configuration->Security->Realm, select <new>
Name: security
Class Name: the one with 'jdbc' in it

JAAS Context: jdbcRealm
JNDI: jdbc/security
User Table: usertable
User Name Column: username
Password Column: password
Group Table: grouptable
Group Name Column: groupid
Digest Algorithm: MD5
<OK>

time to test..

Web Config
Roll Mapping
In Netbeans in the projects tab:
<your war project>->Configuration Files->sun-web.xml and open it
click on <security>
<Add Security Role Mapping>
Security Role Name: USER
<Add Group>
Group Name: USER

and do the same for the admin role/group

web.xml
As I am using JEE 6, I have had no need for the web.xml file as yet, so now I need to create it:
right click on the war project
new->other->Web->Standard Deployment Descriptor

now edit it:
select the 'Security' button/tab
under 'Login Configuration' select Basic
Realm Name: security

then under Security Roles <Add..>
Role Name: USER
do the same for ADMIM role

Now <Add Security Constraint>
Display Name: Test Constraint

Web Resource Collection <Add..>
Resource Name: test
URL Pattern(s): /test/*
<OK>

tick 'Enable Authentication Constraint'
Role Name(s): USER

and save..

create a directory 'test' with a jsp file in it and try access it..

Worked for me, yay me!