Monday, January 12, 2009

NoClassDefFoundError when attempting to use MTOM with NetBeans 6.5 Jax-WS 2.1 client

A Java client that communicates with a .NET host has problems when the HOST has messageEncoding set to MTOM in web.config :

<system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="FileTransferServicesBinding" transferMode="Streamed" 
          messageEncoding="Mtom" maxReceivedMessageSize="10067108864">
        </binding>
      </basicHttpBinding>
    </bindings>
...


(it works fine when messageEncoding="Text")



The client app throws:

    java.lang.NoClassDefFoundError: org/jvnet/mimepull/MIMEMessage



I fixed the problem by downloading from: here and including the mimepull.jar in the libraries.



[One can only hope that the data is transmitted to the host with MTOM encoding]



MTOM stands for Message Transmission Optimization Mechanism, it's explained here.

NetBeans 6.5: Problems building WSDL Java apps after upgrade from 6.1

I recently updated to NetBeans 6.5 and a WEB Services client stopped working. At build time I got the following error message:

C:\myproject\JavaApplication29\nbproject\jaxws-build.xml:10: taskdef A class needed by class com.sun.tools.ws.ant.WsImport cannot be found: com/sun/istack/tools/ProtectedTask

I fixed the problem by adding:
   <classpath path="${libs.jaxb.classpath}"/>
to jaxws-build.xml

 <target name="wsimport-init" depends="init">
  <mkdir dir="${build.generated.dir}/wsimport/client"/>
  <mkdir dir="${build.generated.dir}/wsimport/binaries"/>
  <taskdef name="wsimport" classname="com.sun.tools.ws.ant.WsImport">
    <classpath path="${libs.jaxws21.classpath}"/>

    <classpath path="${libs.jaxb.classpath}"/>
  </taskdef>
 </target>



Saturday, January 10, 2009

Jetty embedded WEB server offers GZIP compression out of the box

Jetty is an Open Source WEB server written Java that you can embed in your application. You can find out more about it here.

It's very useful as an a way to serve a browser based control panel for an appliance, desktop system tray or windows service application that generally runs unattended.
image
Browser based control panels are particularly useful if the application is running in a dark room with no direct access.

My ADSL modem has a good looking, intuitive menu based control panel and if I had a manual it's long lost.
image

"One key benefit of such a solution (written in Java) is that static HTML, XML, JavaScript and CSS files can all be included within the application jar file where they can't be tampered with by the casual user."

An AJAX based solution gives users a nice ride and quick feedback but the the EXT/JS framework that I use has some pretty big js, css, and image files to slow things up.

If you've read my posts about GZIP compression (Safari Browser provides Web Inspection Tool, Use Fiddler to explore HTTP GZIP compression and Fiddler helps you analyse HTTP traffic) you know that download bandwidth can be improved dramatically. This can improve download speed.

  1. Before Using this code 
    imagethe download cost 675KB & 1.46 seconds
  2. After using the the GzipStream instead
    image the download cost 203KB & 1.37 seconds

A saving of 472KB but only 90ms, probably because it's running on localhost where the operating system is passing data via pipes and streams.
The speed improvement on LAN or WWW would probably be significant.

Jetty makes the decisions

Of course we could GZIP the files and include them in the JAR file but the variations in browsers make decision making about which file to serve pretty difficult.

Doing it on the fly makes sense, particularly if your service up large dynamic data files or images.

Here's the code for you to cut:

byte[] b = readResource(uri);
OutputStream os = response.getOutputStream();
// GzipStream os = new GzipStream(request, response, b.length, 8192, 100);
os.write(b);
os.close();
if (uri.endsWith(".js")) {
   response.setContentType("text/javascript");
} else {
   String s = mt.getMimeByExtension(uri).toString();  
   response.setContentType(s);
}
response.setStatus(status);

Another improvement would be to set the last modified header.

This code does not work:
image because f.lastModified() returns 0.
Maybe because it's a resource.

When we go live, we can use the date on the jar file, but that's no good during debugging.

I'll post the solution when I find it.

Friday, January 9, 2009

Safari Browser provides Web Inspection Tool

In previous posts, Use Fiddler to explore HTTP GZIP compression and Fiddler helps you analyse HTTP traffic I mentioned tools to analyse how you web site.

The Safari browser offer the same detail, but in a more visual user interface. (Download it here.)

Safari WEB Inspector

Click on a page name, and you get the content:
image

Click on the time bar and it accordion opens the details:
image 

It makes suggestions about improving throughput.

"If your developing WWW sites, you probably need Safari installed anyway.

"Why not use it to improve your throughput."

Sunday, January 4, 2009

Sorting photo data from Google's Picasa API

In the earlier post Google Calendar Service hosts a Gig Guide, I showed how to to use Google Data APIs to use Google Calendar data.

SNAG-1075The client has posted photos of interest to Picasa and given them tags that determine which page of their WEB site they are displayed on.

This post describes

  • how to use the Picasa Service to host photos and use them in a ASP.NET WEB site using VB
  • how to sort the GData feed into "caption" sequence using a .Net IComparer
  • how to avoid possible problems when the application is hosted on a virtual WEB hosting site like GoDaddy.

The software uses the Picasa API which should be downloaded and installed.

  1. Install the DLLs in the Bin folder of your ASP.NET application
  2. If you are having the app hosted in a shared environment, make sure you use the DLL's in the ASP.NET folder
  3. You must also use the 1.2.2 (or later) version of the API. Earlier versions do not work.
  4. If you are in a shared environment, you will also need to set the MSBuild properties as follows of the VB.NET app as follows:
    image

In the code snippet below, the class:

  1. Connects to the service and sets user credentials
  2. genHTML(tag) returns a HTML table for that tag, by constructing the string from the entries returned by getPhotoFeed(tag, size) and getComment(entry)
  3. getPhotoFeed builds the query and retieves the results
    It also sorts these using, myEntryCompare - our specialist implementation of IComparer
Imports Microsoft.VisualBasic
Imports Google.GData.Photos
Imports Google.GData.Client
Imports Google.GData.Extensions
Imports Google.GData.Extensions.Location

Public Class FoodList
Dim password As String = "secret"
Dim username As String = "secret"
Dim albumName As String = "WWW"
Dim service As PicasaService
Public Sub New()
service = New PicasaService("more secrets")
service.setUserCredentials(username, password)
End Sub
Public Function getMenuHTML(ByVal tag As String) As String
Dim html As String = "<table>"
For Each entry As PicasaEntry In getPhotoFeed(tag, "320")
html += "<tr><td colspan=2><h4>" + entry.Summary.Text + "</h4><tr><td><img src='" + entry.Content.AbsoluteUri + "'/> <td>" + getComment(entry)
Next
html += "</table>"
Dim x = ""
Return html
End Function
Private Function getPhotoFeed(ByVal tag As String, ByVal size As String) As ArrayList
Dim query As New PhotoQuery(PicasaQuery.CreatePicasaUri(username, albumName))
query.Tags = tag
query.ExtraParameters = "imgmax=" + size + "u" ' set the maximum size of the returned images
Dim feed As PicasaFeed = service.Query(query)
Dim arr As New ArrayList
arr.AddRange(feed.Entries) ' add all the entries to an array
arr.Sort(New myEntryCompare) ' sort the entries by the caption (Summary.Text)
Return arr
End Function
Public Class myEntryCompare
Implements IComparer
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
Dim myX As String = DirectCast(x, PicasaEntry).Summary.Text
Dim myY As String = DirectCast(y, PicasaEntry).Summary.Text
Return myX.CompareTo(myY)
End Function 'IComparer.Compare
End Class
Private Function getComment(ByVal photoEntry As PicasaEntry) As String
Dim query As CommentsQuery = New CommentsQuery(PicasaQuery.CreatePicasaUri(username, albumName, (New PhotoAccessor(photoEntry)).Id))
Dim feed As PicasaFeed = service.Query(query)
Try
Return feed.Entries(0).Content.Content
Catch ex As Exception
End Try
Return ""
End Function
End Class