I created a sudoku solver using candidate elimination together with back tracking. That in itself is quite nice, but I also exposed the solver to the world in the form of a web service. What does that mean?

If you go to this url:
http://82.93.220.122:8080/SudokuWS/ws?wsdl
You will be able to download the wsdl file describing the webservice. It describes the sudoku soap message you have to send and the soap message you will get back. Using many tools you can create a client to use this web service or use a general purpose web service viewer.

If you send this soap message:

   1: <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:q0="http://ws.sudoku.enigmatry.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   2:   <soapenv:Body>
   3:     <q0:sudoku>
   4:       <row>
   5:         <cell>0</cell>
   6:         <cell>0</cell>
   7:         <cell>4</cell>
   8:         <cell>7</cell>
   9:  
  10:         <cell>9</cell>
  11:         <cell>0</cell>
  12:         <cell>6</cell>
  13:         <cell>0</cell>
  14:         <cell>0</cell>
  15:       </row>
  16:  
  17:       <row>
  18:         <cell>0</cell>
  19:         <cell>0</cell>
  20:         <cell>0</cell>
  21:         <cell>0</cell>
  22:         <cell>0</cell>
  23:  
  24:         <cell>0</cell>
  25:         <cell>0</cell>
  26:         <cell>0</cell>
  27:         <cell>4</cell>
  28:       </row>
  29:       <row>
  30:         <cell>0</cell>
  31:  
  32:         <cell>2</cell>
  33:         <cell>9</cell>
  34:         <cell>0</cell>
  35:         <cell>0</cell>
  36:         <cell>0</cell>
  37:         <cell>0</cell>
  38:  
  39:         <cell>8</cell>
  40:         <cell>0</cell>
  41:       </row>
  42:       <row>
  43:         <cell>0</cell>
  44:         <cell>1</cell>
  45:         <cell>0</cell>
  46:  
  47:         <cell>0</cell>
  48:         <cell>4</cell>
  49:         <cell>0</cell>
  50:         <cell>0</cell>
  51:         <cell>0</cell>
  52:         <cell>8</cell>
  53:  
  54:       </row>
  55:       <row>
  56:         <cell>0</cell>
  57:         <cell>3</cell>
  58:         <cell>0</cell>
  59:         <cell>0</cell>
  60:         <cell>0</cell>
  61:  
  62:         <cell>0</cell>
  63:         <cell>0</cell>
  64:         <cell>5</cell>
  65:         <cell>0</cell>
  66:       </row>
  67:       <row>
  68:         <cell>4</cell>
  69:  
  70:         <cell>0</cell>
  71:         <cell>0</cell>
  72:         <cell>0</cell>
  73:         <cell>2</cell>
  74:         <cell>0</cell>
  75:         <cell>0</cell>
  76:  
  77:         <cell>3</cell>
  78:         <cell>0</cell>
  79:       </row>
  80:       <row>
  81:         <cell>0</cell>
  82:         <cell>8</cell>
  83:         <cell>0</cell>
  84:  
  85:         <cell>0</cell>
  86:         <cell>0</cell>
  87:         <cell>0</cell>
  88:         <cell>5</cell>
  89:         <cell>7</cell>
  90:         <cell>0</cell>
  91:  
  92:       </row>
  93:       <row>
  94:         <cell>2</cell>
  95:         <cell>0</cell>
  96:         <cell>0</cell>
  97:         <cell>0</cell>
  98:         <cell>0</cell>
  99:  
 100:         <cell>0</cell>
 101:         <cell>0</cell>
 102:         <cell>0</cell>
 103:         <cell>0</cell>
 104:       </row>
 105:       <row>
 106:         <cell>0</cell>
 107:  
 108:         <cell>0</cell>
 109:         <cell>1</cell>
 110:         <cell>0</cell>
 111:         <cell>6</cell>
 112:         <cell>8</cell>
 113:         <cell>2</cell>
 114:  
 115:         <cell>0</cell>
 116:         <cell>0</cell>
 117:       </row>
 118:     </q0:sudoku>
 119:   </soapenv:Body>
 120: </soapenv:Envelope>
 121:  

 

You will receive this message in return, with the solved sudoku:

   1: <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
   2:   <env:Body>
   3:     <sudokuResult xmlns:ns1="http://ws.sudoku.enigmatry.com/">
   4:       <solved>true</solved>
   5:       <sudoku>
   6:         <row>
   7:           <cell>8</cell>
   8:           <cell>1</cell>
   9:  
  10:           <cell>3</cell>
  11:           <cell>6</cell>
  12:           <cell>7</cell>
  13:           <cell>4</cell>
  14:           <cell>9</cell>
  15:           <cell>2</cell>
  16:  
  17:           <cell>5</cell>
  18:         </row>
  19:         <row>
  20:           <cell>5</cell>
  21:           <cell>6</cell>
  22:           <cell>2</cell>
  23:           <cell>1</cell>
  24:  
  25:           <cell>3</cell>
  26:           <cell>9</cell>
  27:           <cell>8</cell>
  28:           <cell>4</cell>
  29:           <cell>7</cell>
  30:         </row>
  31:  
  32:         <row>
  33:           <cell>4</cell>
  34:           <cell>7</cell>
  35:           <cell>9</cell>
  36:           <cell>5</cell>
  37:           <cell>2</cell>
  38:  
  39:           <cell>8</cell>
  40:           <cell>6</cell>
  41:           <cell>3</cell>
  42:           <cell>1</cell>
  43:         </row>
  44:         <row>
  45:           <cell>7</cell>
  46:  
  47:           <cell>8</cell>
  48:           <cell>4</cell>
  49:           <cell>3</cell>
  50:           <cell>1</cell>
  51:           <cell>6</cell>
  52:           <cell>2</cell>
  53:  
  54:           <cell>5</cell>
  55:           <cell>9</cell>
  56:         </row>
  57:         <row>
  58:           <cell>9</cell>
  59:           <cell>5</cell>
  60:           <cell>1</cell>
  61:  
  62:           <cell>4</cell>
  63:           <cell>8</cell>
  64:           <cell>2</cell>
  65:           <cell>3</cell>
  66:           <cell>7</cell>
  67:           <cell>6</cell>
  68:  
  69:         </row>
  70:         <row>
  71:           <cell>3</cell>
  72:           <cell>2</cell>
  73:           <cell>6</cell>
  74:           <cell>7</cell>
  75:           <cell>9</cell>
  76:  
  77:           <cell>5</cell>
  78:           <cell>4</cell>
  79:           <cell>1</cell>
  80:           <cell>8</cell>
  81:         </row>
  82:         <row>
  83:           <cell>6</cell>
  84:  
  85:           <cell>3</cell>
  86:           <cell>7</cell>
  87:           <cell>9</cell>
  88:           <cell>4</cell>
  89:           <cell>1</cell>
  90:           <cell>5</cell>
  91:  
  92:           <cell>8</cell>
  93:           <cell>2</cell>
  94:         </row>
  95:         <row>
  96:           <cell>1</cell>
  97:           <cell>9</cell>
  98:           <cell>8</cell>
  99:  
 100:           <cell>2</cell>
 101:           <cell>5</cell>
 102:           <cell>3</cell>
 103:           <cell>7</cell>
 104:           <cell>6</cell>
 105:           <cell>4</cell>
 106:  
 107:         </row>
 108:         <row>
 109:           <cell>2</cell>
 110:           <cell>4</cell>
 111:           <cell>5</cell>
 112:           <cell>8</cell>
 113:           <cell>6</cell>
 114:  
 115:           <cell>7</cell>
 116:           <cell>1</cell>
 117:           <cell>9</cell>
 118:           <cell>3</cell>
 119:         </row>
 120:       </sudoku>
 121:     </sudokuResult>
 122:  
 123:   </env:Body>
 124: </env:Envelope>

 

Pretty cool, isn't it? If you can write a nice looking frontend for this webservice, in the form of another website, or a desktop client app, please drop me an email. I would really love to see it!

This article is in dutch, sorry...

Subversion heeft een standaard structuur voor elke repository die er zo uit ziet:

repo1

De root is een url, en die kan niet hoger worden opgevraagd, bijvoorbeeld: https://192.168.1.17:8443/svn/javaTraining

Onder de trunk worden de nieuwste sources ingecheckt. Als dat 1 project is kunnen de directories en files onder de root van het project hier meteen ingezet worden. Zijn het meerdere projecten, dan moeten de roots van de projecten hier ingezet worden. Dit is alleen zo gewenst als de projecten ook echt afhankelijk van elkaar zijn en niet worden gedeeld tussen verschillende projecten, dan moeten die in hun eigen repository worden opgeslagen.

Versienummering

Naast de build nummers worden er door mensen bedachte versie nummers gebruikt. De ideale situatie is dit. Er is een major en een minor nummer. Het minor nummer wordt bij elke (test) release met 1 verhoogd. Het major versienummer wordt verhoogd indien er een grote aanpassing van de code plaats vindt of wanneer er aan 2 versies tegelijk moet worden gewerkt. Dat is bijvoorbeeld bij het ingebruik nemen van de software door de klant met de benodigde bugfixes die soms nodig kunnen zijn en de ontwikkeling van een nieuwe versie. Als dat het geval is dan vind de ontwikkeling van de nieuwe versie plaats in de trunk en de bugfixes in de andere versie in de branch. Het maken van de branch kan uitgesteld worden tot dat er daadwerkelijk een bug optreed die opgelost moet worden in een huidige versie terwijl er ook wordt ontwikkeld aan een nieuwe versie.

De naam van de branch bevat alleen het major versie nummer. Als er een release plaats vindt, dan wordt die neergezet in de tags directory, inclusief het minor nummer.

Voorbeeld

Major versie nummer is: 1.1
Minor versie nummer is: 4
Complete huidige versie is 1.1.4
We vergeten even alle andere vorige versies, dit is de huidige situatie in de repository:

repo2

In de trunk wordt doorgewerkt aan de nieuwe versie, het is nog niet bepaald welke versie dat gaat worden. Maar ondertussen is 1.1.4 wel in productie bij een groot bedrijf en op een gegeven moment vinden ze een bug. Het is makkelijk te verhelpen. De ontwikkelaar die checkt versie 1.1.4 uit van svn en repareert de bug. Tags zijn read-only, dus de fix kan hij niet zomaar inchecken. Hij kopieerd 1.1.4 naar de branches folder in svn en noemt de branch 1.1. En commit zijn code in die branch. Nu ziet het er zo uit:

repo3

Versie 1.1.5 kan gereleased worden naar de klant en iedereen is blij. Als er nu een 2de bug wordt gevonden wordt er een 1.1.6 versie gemaakt van de branch enzovoort. Deze bugs moeten ook worden opgelost in de versie in de trunk! Anders heeft een volgende nieuwe versie ineens weer oude bugs. Dit kan vaak makkelijk gedaan worden met behulp van de merge functie of patch functie van svn/Eclipse .

Twee maanden later is de nieuwe versie af waaraan wordt gewerkt in de trunk. En een eerste versie wordt opgeleverd voor de test. Deze versie kan niet major versie 1.1 krijgen omdat er dan gaan oplopende volgorde van features meer aanwezig is. Dus wordt het versie 1.2 of als er hele grote veranderingen zijn 2.0. Laten we het houden op 1.2. De eerste test versie wordt dan 1.2.0, er wordt nog geen branch gemaakt, omdat de trunk nu als branch fungeert:

repo4

Nu bestaan versie 1.2.x en 1.1.x naast elkaar en kunnen afzonderlijk gereleased worden.

I was interviewed for a project two weeks ago and got accepted soon after. I will be working in Rotterdam to build a new website. The new site will replace the old site, which is not even live yet. This will be my first (or actually second, but first big) project outside the company so although there are many bad smells, I want to see it happen with my own eyes. Bad smells are triggers which tell you the project is crap and will stay crap. What were the smells I smelled:

  • The site which will be replaced is not in operation at this moment
  • The project will probably use old technologies with some buzzwords sprinkled in:
    • old tech:
      • Struts as web framework instead of JSF or even Stripes, or Struts 2.0
      • iBatis as O/R mapper, instead of Hibernate or JPA
    • new tech (= buzzwords):
      • Web Services, but not only to communicate to the outside world, but for use between the front and the back-end inside the same VM!
      • EJB 3, what is the use of EJB 3 if you are using iBatis instead of JPA or Hibernate?

Anyway, EJB 3 might be the only good thing about the whole project. I know EJB 3 quite well at the moment. We will be developing in Oracle JDeveloper, for the Oracle application server and using the Oracle Portal framework. I never heard of Portal before, but they told me that is the reason they will be using Struts. I still have to get a good answer why JPA or Hibernate would be worse than iBatis. And the Web Service idea is completely back wards.

Web Services are intended to expose a certain API to the outside world in a language independent manner so others can use your service. I underlined several words in that last sentence because they highlight the problem I have with using a Web Service inside your own application stack, where you export and consume your own web service at the same time.

Certain API. You expose a certain high level, coarse (not fine grained) API, which is designed to be as easy to use as possible and have the minimum amount of operations possible. This API is hard to change when others start using the web service you are exporting, because they are dependent on it.

Outside world. Your web service is to be used by the outside world. Not by yourself. See the previous argument.

Language independent. Web services can be used and exported in any programming language, programming system and operating system. This is possible because they use XML to define all communications. This means that every time you call a web service, or your webservice is called, you have to generate and decode XML messages. These messages will be send over http connections. All these things cause overhead, which will not be a problem when external parties want to communicate with you, because there is no other easier way, but why would you want to incur all this overhead and other problems inside your own system?

For my Java Business Components certification I learned about web services, but I never wrote one. Some projects I worked on used web services, but I only altered some small things, I never wrote one from scratch. So for this new project I wanted to test myself, can I write a web service?

The Library Web Service

In the last blog post I wrote about a small project setup based on three layers. The back end, the front end, and the interface for the back end, used by the front end. For this web service, I would keep the back end and the interface the same, but I would change the front end. It would not be a website, but web service, which will expose the back end interface.

At first I failed. I tried the standard JBoss 4.2.2, but I could not get it to work. I generated all the proxy and skeleton classes but the web service explorer could not connect to it. And clients I created would fail to connect as well. I tried using Glassfish, the open source application server from Sun. But I had to rewrite all my build scripts. And I could not get my ear file through the verifier, which verifiers the correctness of my ear file. At last I installed another webservice stack for JBoss, the Sun Metro implementation and got a lot further, but I had to generate the proxy classes myself for this stack. I can do that, but the native stack from JBoss can do that during deploy time, so I installed that stack and now my web service finally works correctly.

How did I do it?

I started a new Dynamic Web Project inside Eclipse. In that project I created a new class, called com.enigmatry.library.ws.Library which has a method for each of my library service methods. I only have to annotate this class with the @WebService annotation to make it a web service. I changed the web.xml file so it maps everything to my webservice. Now it looks like this:

   1: <?xml version="1.0" encoding="UTF-8"?>
   2: <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   3:     xmlns="http://java.sun.com/xml/ns/javaee"
   4:     xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   5:     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   6:     id="WebApp_ID" version="2.5">
   7:     <display-name>EnigLibWS</display-name>
   8:     <servlet>
   9:         <servlet-name>LibraryWebService</servlet-name>
  10:         <servlet-class>com.enigmatry.library.ws.Library</servlet-class>
  11:         <load-on-startup>1</load-on-startup>
  12:     </servlet>
  13:     <servlet-mapping>
  14:         <servlet-name>LibraryWebService</servlet-name>
  15:         <url-pattern>/*</url-pattern>
  16:     </servlet-mapping>
  17:     <session-config>
  18:         <session-timeout>30</session-timeout>
  19:     </session-config>
  20: </web-app>

The biggest problem I have (or had) is to reference EJB3 Session Beans from the web layer. I have not found a way to let me locate the EJB3 bean from the InitailContext without specifying an explicit JNDI name with the @org.jboss.annotation.ejb.LocalBinding annotation, which is JBoss specific, so prohibits me from writing a portable ear. In the end I used a JBoss specific deployment descriptor, so I do not have to use the annotation.

Inside the META-INF dir inside the ejb3 jar I added jboss.xml:

   1: <jboss>
   2:     <enterprise-beans>
   3:         <session>
   4:             <ejb-name>library</ejb-name>
   5:             <jndi-name>library</jndi-name>
   6:         </session>
   7:     </enterprise-beans>
   8: </jboss>

Now I can get a reference to the library bean by writing:

   1: Library library = (Library) new InitialContext().lookup("library");

JBoss 5.0 will support the @EJB annotation in the web layer to inject EJB3 Session beans. Other application servers already support this, but JBoss is so much easier to work with. Deploying is just a copy operation instead of calling complex ant tasks with difficult class path definitions.

When I build this war file and use it in my ear file instead (or next to my website war) I can access the webservice definition (the wsdl) by going to this url: http://localhost/war-name/?wsdl. This wsdl is generated at deploy time by the JBoss native web service stack. With other stacks you have to use tools to generate this file. This file can now be used to build client applications which can talk with the created web service.

The Client

I wrote a simple proof of concept client, just to see it working outside the web service explorer. I created the required proxy files from the wsdl by using the wsimport tool from JBoss inside my build script, this is my ant target:

   1: <target name="create-proxy" description="Create local port proxy">
   2:     <taskdef name="wsimport" classname="com.sun.tools.ws.ant.WsImport2">
   3:         <classpath>
   4:             <path refid="cp"/>
   5:         </classpath>
   6:     </taskdef>
   7:     
   8:     <mkdir dir="${dist.dir}" />
   9:     <mkdir dir="${build.dir}" />
  10:     <wsimport 
  11:         wsdl="${conf.dir}/library.wsdl" 
  12:         destdir="${build.dir}" 
  13:         sourcedestdir="${src.dir}" 
  14:         keep="true" 
  15:         extension="false" 
  16:         verbose="true" 
  17:         wsdlLocation="http://localhost:8080/EnigLibWS/library?wsdl" 
  18:         package="com.enigmatry.library.ws">
  19:     </wsimport>
  20: </target>

This creates several files which can then be used to talk with the web service. The only code I had to write is this:

   1: public static void main(String[] args) {
   2:     try {
   3:         LibraryService service = new LibraryService();
   4:         System.out.println("Retrieving the port from the following service: " + service);
   5:         Library library = service.getLibraryPort();
   6:         System.out.println("Invoking the getVersion operation on the port.");
   7:         String response = library.getVersion();
   8:         System.out.println(response);
   9:         BookArray books = library.searchBook(new SearchBookVO());
  10:         for(Book book : books.getItem()){
  11:             System.out.println(book);
  12:         }
  13:     } catch(Exception e) {
  14:         e.printStackTrace();
  15:     }
  16: }

As you can see, I create a new LibraryService object (line 3)  and get the port from this service to get a proxy object (line 5) which I can use to call the web service. All these objects are generated by the wsimport tool. Then I can just call the web service as it is my normal interface (line 7 and 9).

There are some things different though. I could not use a Set<Book> as a return type in my web service, so I used a Book[], but as you can see in the client, the method will return a BookArray type (line 9). Which returns a List<Book> if you call getItem() on it (line 10).

It is probably possible to change the generated files so it returns a Book[] directly, but that will involve a lot of hand coding.

"Can you please set up a default Java development environment for us?", my boss asked me a while ago.  That's a pretty difficult question, because where I come from, everyone is allowed to set up his or her own environment. Some people use NetBeans, others use a old version of Eclipse while others use the newest beta version of Eclipse with all possible plugins installed. Everyone should be able to customize his or her tools as they see fit. But I do agree, there should be some consistency across developers and projects.

So I started to set up a new fresh Java project with several goals in mind:

  • Create a play environment to learn web development and server development for the java certifications, mostly the web components and business components certifications.
  • Make a setup which can be used as a template for future projects.
  • Test my ability to start a project from scratch, which I have never done before.
  • Test the tooling of Eclipse to help me do it.

The Project

I decided to start a small web application which used a Stripes front end, a JPA/Hibernate domain model and an EJB3 backend. As I said, I never started a project before, so I asked Eclipse to help me. The normal way to organise these things in Eclipse is to have a separate project for each layer in the same workspace. I created these projects:

  • A Dynamic Web project
    Holds the webpages and the view code logic. Simple applications can be implemented in only this project.
  • An EJB project
    Holds the business layer of the application, the Enterprise Beans, is completely independent of the view layer.
  • An EJB Client project (gets created by the EJB project)
    Defines the interface of the business layer used by the view layer. Also contains the JPA Entity classes and the value objects, which are used in the interface of the business layer.
  • An Enterprise Application Project
    Does not contain any code, but wraps the other 3 projects together in an .ear file to be deployed on a J2EE application server.

This setup compiles and packages the files in the correct manner and deploys it to the J2EE server configured in Eclipse. This is all nice and simple, but this removes the ability for a developer to set up his or her own environment because the build process is now dependent on Eclipse, the environment. So I created an Ant build script to build, package and deploy the whole project without Eclipse. I also could have used Maven, but my experience with Maven is that it is slow to build and hard to configure and even harder to track down problems when they exist. Also missing dependencies, old online repositories and different versions causing problems made me choose for Ant.

Building and Deploying

I think the manner Eclipse chooses to setup of these kind configurations is pretty smart. To be able to build another kind of web front on the same backend, one only has to switch out the web project, tweak the ear project to package the new web project and everything should work. It is also possible to build a Swing client or some webservice, all using the same backend. So I tried mimic this approach in the ant script.

Short interlude: an ear file is an enterprise archive, which contains other archives which when combined consist of an application which can run on an application server, like JBoss, Geronimo, Glashfish and others. The other archives are jars and wars, which are Java archives or web archives. A Java archive contains java classes and configuration files, and can contain enterprise java beans of various sorts and normal Java code. Web archives contain web pages in the form of servlets, which can be a jsp pages or other static content and can also include normal Java classes.

Each project has its own ant script which builds the project and places an .jar or a .war file in the 'dist' folder of the project. The ear project has a special ant script which invokes the other scripts and packages the created .war and .jar files in an .ear file. The ear script decides on which projects to call (so the developer can rename the projects or check them out to a different directory) on a '.ant-global.properties' file, which should be present in the workspace directory. This file also holds a reference to the JBoss instance, so the ear file can be deployed by the script as well.

Importing Libraries in Eclipse and Ant

Applications build on a J2EE platform depend on many libraries. This are jar files which hold common code used by the system, for example logging, Hibernate and Stripes. In the past, projects had different ways to acquire the correct libraries used by the application. When Maven was used, they would let Maven handle these dependencies, which would work correctly, until the repository was not available anymore. Also checking these dependencies would take ages every time the project would be build. Other projects used Ant to checkout the used libraries from a special library project which would hold all the libraries for each project.
Eclipse uses the reference to the application server to reference all its libraries. So I realized I don't need to download 20+ jar files in my build script somehow, I can use the ones in the JBoss server. And I also have a reference to the JBoss environment for the deploy feature of my script. Now I only have to manage the libraries which are not bundled in application servers, which are not many. So far I have the JSTL core library and the Stripes library in the application, and for now I just check those in the repository so they are downloaded and managed directly with the project. Only problem is duplicate entries of the same jar file in many future projects, but as disk space becomes cheaper almost every minute, this won't be a problem for now, or maybe ever.