Tomer Gabel's annoying spot on the 'net RSS 2.0
# Sunday, 09 December 2007

Quick link: download

In my work at Semingo I often encounter situations where it's impossible to unit- or integration-test a component without accessing the web. This happens in one of two cases: either the component itself is web-centric and makes no sense in any other context, or I simply require an actual web server to test the components against.

Since I firmly believe that tests should be self-contained and rely on external resources as little as possible, a belief which also extends to integration tests, I wrote a quick-and-dirty pluggable web server based on the .NET HttpListener class. The unit-tests for the class itself serve best to demonstrate how it's used; for instance, the built-in HttpNotFoundHandler returns 404 on all requests:

    [Test]
    [ExpectedException( typeof( WebException ) )]
    [Description( "Instantiates an HTTP server that returns 404 on all " +
"
requests, and validates that behavior." )] public void VerifyThatHttpNotFoundHandlerBehavesAsExpected() { using ( LightweightWebServer webserver =
new LightweightWebServer( LightweightWebServer.HttpNotFoundHandler ) ) { WebRequest.Create( webserver.Uri ).GetResponse().Close(); } }
The web server randomizes a listener port (in the range of 40000-41000, although that is easily configurable) and exposes its own URI via the LightweightWebServer.Uri property. By implementing IDisposable the scope in which the server operates is easily defined. Exceptions thrown from within the handler are forwarded to the caller when the server is disposed:
    [Test]
    [ExpectedException( typeof( AssertionException ) )]
    public void VerifyThatExceptionsAreForwardedToTestMethod()
    {
        using ( LightweightWebServer webserver = new LightweightWebServer(
            delegate { Assert.Fail( "Works!" ); } ) )
        {
            WebRequest.Create( webserver.Uri ).GetResponse().Close();
        }
    }
The handlers themselves receive an HttpListenerContext, from which both request and response objects are accessible. This makes anything from asserting on query parameters to serving content trivial:
    [Test]
    public void VerifyThatContentHandlerReturnsValidContent()
    {
        string content = "The quick brown fox jumps over the lazy dog";

        using ( LightweightWebServer webserver = new LightweightWebServer(
            delegate( HttpListenerContext context )
                {
                    using ( StreamWriter sw = new StreamWriter( context.Response.OutputStream ) )
                        sw.Write( content );
                } ) )
        {
            string returned;
            using ( WebResponse resp = WebRequest.Create( webserver.Uri ).GetResponse() )
                returned = new StreamReader( resp.GetResponseStream() ).ReadToEnd();

            Assert.AreEqual( content, returned );
        }
    }

We use this class internally to mock anything from web services to proxy servers. You can grab the class sources here -- it's distributed under a Creative Commons Public Domain license, so you can basically do anything you want with it. If it's useful to anyone, I'd love to hear comments and suggestions!

Sunday, 09 December 2007 23:18:06 (Jerusalem Standard Time, UTC+02:00)  #    -
Development

Well the title is actually a semi-private joke, but the point of the post is to draw attention to long-time friend, coworker and Mentor cofounder Shlomo Priymak's new blog. Shlomo is our sharp-but-misanthropic DBA, precisely the kind of person you'd want to pay attention to for hardcore MySQL (and other) problems and solutions.

Speaking of Semingo, we're gearing up to a relatively close alpha launch and have a new corporate website. Now would still be a good time to hop on the bandwagon and join a fast-growing company made up entirely of crazy-ass people out to do the implausible. If this sounds right to you, get in touch! (keywords: web 2.0 startup search .net java developers qa algorithms nlp and others)

Sunday, 09 December 2007 16:17:27 (Jerusalem Standard Time, UTC+02:00)  #    -
Personal
# Thursday, 06 December 2007

With the web host giving me trouble, just to add insult to injury the comment captcha generator stopped working. I don't know how long it's been this way and I sincerely hope it's a new problem; at any rate I disabled captchas and added Akismet spam filtering in the hopes that it'll keep everyone comfortable and the blog clear of spam...

I'm getting a little tired of all these issues with dasBlog (I still haven't been successful in configuring it on the new webhost) and am seriously considering replacing it; I'm basically really happy with the application, but configuration and installation issues are taking a little too much of my time. If anyone has an easy-to-use platform in mind, preferably one with a straightforward migration path, I'd appreciate the suggestion.

Thursday, 06 December 2007 11:17:16 (Jerusalem Standard Time, UTC+02:00)  #    -
Personal
# Wednesday, 05 December 2007

So you want a web project, a build system and a reasonable IDE to take care of the annoying details for you, right? The good news are that it's actually quite possible and there're many ways to do this. The bad news are that it's nigh impossible to get them to play along if you don't already know how to do that. It took me days to find a solution that finally seems to work, and I'd like to share it with you. I'm probably missing a few important details or did something really really stupid along the way (I'd appreciate comments!), but this process does seem to work. I'm not going into essentials of Java-based web development here -- if you want a more basic explanation of the terminology, post a comment and I'll see what I can do...

1. Goal

I want to:

  • Use a common, standard and powerful IDE to edit and debug my Java code, and preferably provide a usable GUI interface for dependency and project management;
  • Use a common, standard servlet container to host my servlet and still be able to control and debug everything from the same IDE;
  • Have a convenient way to handle internal-and-external dependencies without worrying too much about the details;
  • Be able to quickly compile, test and package my servlet for deployment;
  • Understand as little as possible about the dependency stack of the tools involved

I'm going to tell you how to achieve most of these goals, with two glaring omissions: I won't show you how to do testing (I haven't successfully managed servlet unit testing so far -- different post on that) and I can't help but delve into some of the more annoying details involved with these tools and their dependencies. Sorry about that. Additionally, some of the information here applies even if you use different tools, but you're bound to face issues not covered here; don't assume I know more than you do -- seek the answers, post them somewhere, and maybe the next person will actually find what they're looking for!

2. Tools

The tools used are:

  • J2SE JDK is an obvious must-have. Version used: JDK 6 update 3;
  • Eclipse (but please don't download it just yet) for code editing, debugging and project management;
  • Web Tools Platform: this is the Eclipse plug-in that adds web development capabilities to the IDE, including J2EE dependency management, hosting and running servlets from within the Eclipse workspace etc. This would be a good time to run along to the WTP web-site and download the Web Tools Platform All-In-One package. I only used the release version (2.0.1 at the time of writing this), so if you use another version your mileage may vary;
  • Apache Maven is the newfangled build system from Apache slated to replace ant. I've used it for the last few days and so far it appears to be quite robust and even fairly well-integrated into Eclipse (see next item). Version used: 2.0.8;
  • M2eclipse is the Eclipse plug-in for Maven integration. I've only found one problem with it so far, which I'll detail later on;
  • Apache Tomcat is a solid choice in servlet containers. It's robust, fast and open-source, and has terrific Eclipse integration. I haven't given any of the other containers a serious whirl yet though.

3. Preparations

Unlike Visual Studio, with the tools mentioned above there's no straightforward installation procedure. You'll have to designate at least a workspace directory (where your Eclipse projects, settings etc. go) and some location where the tools themselves go. For me, it's C:\Dev\Eclipse and C:\Tools respectively.

  • Setting up Java:
    • Install the JDK and remember where it was installed (nominally in %PROGRAMFILES%\Java\jdk1.6.0_03)
    • Set up a system-wide JAVA_HOME environment variable pointing to the same directory
  • Setting up Maven and Tomcat:
    • Extract both archives to your designated directory (e.g. for Maven it would be C:\Tools\apache-maven-2.0.8)
    • Add the Maven bin directory to your PATH environment variable (user- or system-wide, depending on your preference)
    • Add whichever J2EE libraries you desire from the Tomcat installation to your class-path. If you have no idea what I'm talking about, you'll probably just want to set the CLASSPATH environment variable to your equivalent of c:\tools\apache-tomcat-6.0.14\lib\servlet-api.jar;c:\tools\apache-tomcat-6.0.14\lib\jsp-api.jar
  • Setting up Eclipse:
    • Extract the WTP all-in-one package (which contains Eclipse itself) to your designated directory (e.g. C:\Tools\Eclipse)
    • Load Eclipse and point it to your designated workspace location
    • Install M2Eclipse:
      • Go to Help->Software Updates->Find and Install..., select "Search for new features to install" and click Next
      • Click on New Remote Site..., use M2eclipse or whatever for the name and http://m2eclipse.codehaus.org/update/ for the URL
      • Click on Finish and let Eclipse install the M2Eclipse plug-in
    • Set up a web server runtime for Eclipse to host your servlets in:
      • Open Window->Preferences...
      • Under Server select Installed Runtimes and click on Add...
      • Choose (from Apache) the Apache Tomcat v6.0 runtime and click Next
      • Enter the Apache installation directory (e.g. C:\Tools\apache-tomcat-6.0.14) in the appropriate location and click Finish

4. Creating a new web project

First off, you must create the actual project, directory structure etc. To do this:

  • Open a command prompt, go to your Eclipse workspace directory
  • Decide on your Maven group and artifact IDs; it's worth noting that the artifact ID is also the directory name for the project
  • Type in mvn archetype:create -DgroupId=your.group.id -DartifactId=your.artifact.id -DarchetypeArtifactId=maven-archetype-webapp
  • You'll notice that a new directory was created under the workspace root
  • Edit the Maven project descriptor POM.XML in the newly created directory:
    • Add (after the <url> tag, although I'm not sure the order matters) the following section:
      <profiles>
          <profile>
              <id>servlet</id>
              <activation>
                  <activeByDefault>false</activeByDefault>
              </activation>
              <dependencies>
                  <dependency>
                      <groupId>javax.servlet</groupId>
                      <artifactId>servlet-api</artifactId>
                      <version>2.5</version>
                      <scope>provided</scope>
                  </dependency>
                  <dependency>
                      <groupId>javax.servlet.jsp</groupId>
                      <artifactId>jsp-api</artifactId>
                      <version>2.1</version>
                      <scope>provided</scope>
                  </dependency>
              </dependencies>
          </profile>
      </profiles>
    • Under <build>, add the following section:
      <plugins>
          <plugin>
              <groupId>org.mortbay.jetty</groupId>
              <artifactId>maven-jetty-plugin</artifactId>
          </plugin>
          <plugin>
              <artifactId>maven-compiler-plugin</artifactId>
              <configuration>
                  <source>1.5</source>
                  <target>1.5</target>
              </configuration>
          </plugin>
      </plugins>
  • You can now thank my colleague Aviran Mordo for finding out this bit of Voodoo. :-)
  • In the command prompt, now enter the project directory
  • Type in mvn eclipse:m2eclipse -Dwtpversion=1.5 to create an Eclipse project
  • Run Eclipse if it's not already started, then from the package explorer right click anywhere and click on Import...
  • Choose General->Existing Projects into Workspace. For root directory pick the workspace directory
  • Choose the new project and click on Finish
  • At this point you may encounter a "Java compiler level does not match the version of the installed Java project facet" error. If that's the case, just right-click on the error (in the Problems view) and select Quick Fix, which will allow you to change the Java project facet version to 6.0. If this isn't what you want, you probably know enough to resolve the issue on your own...
  • You'll need a src/main/java directory as a root source folder (as per the Maven convention). Right-click on the project, select New->Source Folder and type in src/main/java.
  • Finally, in order to execute or debug the project on an actual running server, right-click on the new project and select Properties. From there go to the Server tab and select the runtime you created in the previous chapter.

At this point you have a Maven web project with a corresponding Eclipse project ready for editing in your workspace. In practice you will have to do several things to have any meaningful results.

  1. Add your own code into the mix, such as a servlet. When adding a new servlet (via right-clicking the project, New->Other and choosing Web->Servlet) your WEB.XML file is automatically updated with the new servlet.
  2. Add your own dependencies. Maven handles dependencies quite well; for instance, in order to actually create a servlet you're going to need servlet-api.jar in your classpath; the easiest way to do this is to right-click the project, select Maven->Add Dependency and then simply type in servlet and choose javax.servlet servlet-api.
  3. When you wish to run or debug your servlet, right-click on its Java file and select Run As->Run on Server (or Debug, as appropriate). Your applet should be happily up and running.

5. Converting an existing project to Maven

I'm not sure how to go about doing this for web projects, but converting regular projects to use Maven is actually pretty straightforward; move your sources to the appropriate directories according to the Maven conventions, right-click the project in Eclipse and choose Maven->Enable Dependency Management; this will implicitly create a Maven project descriptor for you (POM.XML) and that's pretty much it. From that point on your Eclipse and Maven projects should peacefully coexist, allowing you to leverage both tools for your purposes.

Wednesday, 05 December 2007 15:10:05 (Jerusalem Standard Time, UTC+02:00)  #    -
Development | Java

In case it wasn't obvious, I've been doing some Java development lately. One of the curious things about doing development in the Java world is that, whereas in the Microsoft world you get a fairly complete tool-chain direct from a commercial vendor, in the Java world you're pretty much dependant on the open-source ecosystem built around the essential Java technologies: Sun defines the APIs, the community provides the tools. In many ways this is really really cool: many Java tools like JUnit are so absolutely groundbreaking that they found their way into the common development idiom irrespective of language, and the availability of tools for just about any purpose is a tremendous advantage (being able to choose freely between Resin, Jetty, Tomcat or any other commercial container, for instance, is a huge boon).

This diversity and community-centric development ecosystem definitely comes with a price though. Java tools, even the high-profile ones such as Eclipse, are extremely difficult to work with for the uninitiated, with a learning curve somewhat like that of Linux: if you take the time to learn the tools you can do astounding things and remain in complete control of the system, but the sheer context required to do even the most trivial thing can be - and often is - daunting.

I've been battling these tools on and off for the last few weeks and often end up having to figure something out on my own. Unlike the .NET ecosystem, it's usually quite difficult to find a blog post detailing a solution to a particular problem. To that end I intend to document my successes - victories, if you will - over the tool-chain, and also the problems I encounter and haven't been able to solve, in the hope of helping others and maybe myself in the process. These posts will go under the Development->Java category, and I'd really appreciate any comments on the solutions (so that I can improve my own work) as well as the problems (so I can actually solve them). Here's to hoping :-)

Wednesday, 05 December 2007 11:48:11 (Jerusalem Standard Time, UTC+02:00)  #    -
Development | Java

After losing several days' worth of e-mails (twice) because of issues with my web host provider, I've decided to switch to a different host. That was a few weeks ago; since then I've created hosting accounts with no less than 7 different hosting providers before I settled on a new one. Since then I've been trying to get DasBlog 2.0 to run properly on the new host, so far with little or no success (if anyone can tell me what would case an ASP.NET parser error with a Could not load type 'newtelligence.DasBlog.Web.Global' error message, I'll be forever in your debt :-)).

The point here is that I've refrained from posting new content before I got this all sorted out, but since the process appears to take considerably more time than I thought I'll probably just go ahead and post everything in the coming days. The first meaningful post in a while is coming, stay tuned :-)

Wednesday, 05 December 2007 11:27:42 (Jerusalem Standard Time, UTC+02:00)  #    -
Personal
# Monday, 15 October 2007

I've been developing software with .NET professionally for the last five years or so, and aside from the occasional foray into other languages I've more or less specialized in that environment. While merrily hacking away at our back-end here at Semingo, we've recently made the decision to develop an aspect of said back-end in Java. As it's always a good practice to keep an open mind and experiment with other technologies I've happily accepted the challenge.

After working with Java and its associated tools for the past three or so weeks I have several observations to make:

  1. The prominent free Java IDE, Eclipse, is actually a very full featured and impressive platform but takes a lot of getting used to. Some of the idioms and concepts are radically different than Visual Studio; for instance, whereas in Visual Studio you'd create an "ASP.NET Application Project", in Eclipse you create (or convert to) a dynamic web project and then add something called facet to your project; for instance, a Dynamic Web Module facet allows you to easily create and debug servlets, and the "Axis2 Web Services Core" facet allows you to create Axis2-based web services and work on them from within your IDE. To actually make use of these features, however, one needs to develop a pretty hefty knowledge base on the various technologies involved (J2EE and servlets, servlet containers like Tomcat, WTP etc.)
  2. Eclipse is next-to-useless without some tinkering; in particular, what I originally attributed to very immature web development plug-ins - the WTP umbrella project I already mentioned - turned out to be the default memory settings of the Eclipse launcher. The launcher hosts the Java VM and its baseline configuration is simply inadequate. In my case adding the following switches: -vmargs -Xmx512M -XX:MaxPermSize=128M to the command line resolved all of the problems I had with the various WTP plug-ins, as well as the myriad crashes I've experienced with the IDE. In fact it's rock-stable now.
  3. The Java language has some unexpected caveats; for instance, whereas in C# the designers eschewed fall through in the switch statement (you can group labels to implementations, but you cannot fall through from the implementation of one case statement to the next), the Java designers elected to maintain C-style behavior. I'm of the belief that switch statement fall through is the cause of a huge number of subtle, hard-to-find bugs, and was surprised to learn of this discrepancy between the two languages.
  4. Enumerations in Java, a relatively new feature added in 1.5, are an impressively diverse feature which is a great deal more powerful than its C# counterpart. I only wish the designers would also allow for a more simplified "SOME_CONSTANT = 3" type syntax, as it's somewhat cumbersome to have to actually use constructors for the purpose. Additionally Java does not (to my knowledge) support implicit conversion operators, which makes necessary constructs such as SomeEnum.CONSTANT_VALUE.getConvertedValue(). It's not a huge issue but it's one of the many areas where syntactic sugar in C# is useful.
  5. Speaking of syntactic sugar, there're several aspects where Java simply falls short of C#: disposables, iterators and delegates. Yes, I know delegates are an essentially religious issue for the Java designers (mostly for historical reasons, I suspect), and I won't deny that anything you can do with delegates you can do with nested classes, but at ridiculous verbosity. As for disposables, I find that the using keyword in C# is one of the most useful language constructs I've ever encountered, the use of which goes way beyond the original intention of elegantly scoping unmanaged resource use; finally, iterators are tremendously useful and cut a lot of unnecessary boilerplate code out of the equation.
  6. The Java ecosystem is riddled with code- and buzz-words, to the point of being annoying. If you thought .NET has too many sub-technologies and acronyms, you should try Java. Just to get the taste buds going, here are some of the keywords I've been messing with for the past couple of weeks: J2SE, J2EE, Servlet, Eclipse, WTP, (Apache) Tomcat, Axis2, SAX, JAXP, JAX-RPC, JAR, WAR, AAR, EAR, JDBC, JavaDoc, JSP and JavaBeans. And that's just off the top of my head! To someone with any sort of Java experience this list wouldn't seem intimidating or even exhaustive, but to a new-comer that's simply too much. There is also an import cultural distinction: Visual Studio and its associated technologies (.NET, ASP.NET, ADO.NET etc.) are designed to ease you in as you learn the ropes; I found it much easier to simply start working with them and learn as I go, whereas with the Java counterparts I usually found myself trying to rework code samples found on the 'net while scratching my head.
    Now don't misunderstand me: the Java technologies are generally impressive, mature and usable, but the learning curve is not nearly as comfortable as the competing technologies from Microsoft, and the tools and documentation just aren't as polished.
Monday, 15 October 2007 16:00:15 (Jerusalem Standard Time, UTC+02:00)  #    -
Development
# Tuesday, 02 October 2007

The Windows SDK command shell, setenv.cmd, is immensely useful, so much so that I wanted it as my default command prompt (i.e. when CMD is run, no matter by whom). A quick Google search didn't turn out anything, so I eventually figured it out myself. The trick is to add it to the command processor's AutoRun value in the registry (run cmd /? from the command prompt if you don't know what I'm talking about):

reg add "HKCU\Software\Microsoft\Command Processor" /v AutoRun /f /t REG_EXPAND_SZ /d "\"%programfiles%\Microsoft SDKs\Windows\v6.0\Bin\SetEnv.Cmd\" /debug /x86 /vista"

You'll notice that I explicitly set the arguments for setenv.cmd; I can't explain it (nor bothered to delve into the script), but without these arguments the script gets stuck along with the command prompt. You should obviously change the values to your own environment.

Tuesday, 02 October 2007 21:18:28 (Jerusalem Standard Time, UTC+02:00)  #    -
Development | Software
# Tuesday, 25 September 2007

Our test code makes extensive use of a lightweight web server implementation based on the .NET 2.0 HttpListener class (I'll write a separate post on this, source code included, in the near future). The web server implementation randomizes an incoming port and exposes its URI via a property; this is done for two reasons: to avoid conflicts with other listeners on the machine, and to facilitate multi-threading test runners (like TestDriven.NET). Even with a serial test-runner (like ReSharper's unit test driver, which is what most of us at Semingo use) this means that for an average test suite a large number of web-server instances are initiated and disposed of in very rapid succession. Our implementation creates each web server on its own dedicated port (using Socket.Bind to make sure that the port is free), and closes the listener via HttpListener.Abort when the test ends.

Although the entire test suite seems to run fine locally (both via ReSharper's test runner and using NUnit-Console), at some point we started getting strange errors from our continuous integration server:

System.Net.HttpListenerException : Failed to listen on prefix 'http://+:40275/' because it conflicts with an existing registration on the machine.

I verified this by running the tests with NUnit-Console on the CI server and it was consistent (but only on that server). Since the only instances of HttpListener used throughout the test suite are those used by the test web servers, this means that HttpListener.Abort does not properly unregister its own prefixes. Since the documentation for HttpListener is rather sparse and I couldn't find any mention of this issue on the web, I eventually went the Reflector route. Check out the Reflector decompiler output for both HttpListener.Dispose (called via Close) and HttpListener.Abort methods:

HttpListener.Dispose

HttpListener.Abort

if (this.m_State != State.Closed)
{
    this.Stop();
    this.m_RequestHandleBound = false;
    this.m_State = State.Closed; 
}
if (this.m_RequestQueueHandle != null)
{
    this.m_RequestQueueHandle.Abort();
}
this.m_RequestHandleBound = false;
this.m_State = State.Closed;

The primary difference is the call to HttpListener.Stop. Here's a code snippet from that method:

if (this.m_State != State.Stopped)
{
    this.RemoveAll(false);
    this.m_RequestQueueHandle.Close();
    this.m_RequestHandleBound = false;
    this.m_State = State.Stopped;
    this.ClearDigestCache();
}

I'll spare you the hunt and point the problem out. There are two tangible differences between the two calls:

  1. HttpListener.Close closes the request queue handle (which is used in calls to the native HTTP API) whereas HttpListener.Abort aborts it. I didn't delve into this but the semantics seem to be the same as for the HttpListener itself.
  2. HttpListener.Close calls RemoveAll before disposing of the queue handle, presumably in order to stop accepting incoming requests.

In order to solve the problem, you can either remove the prefixes manually, or call the internal method like so:

listener.GetType().InvokeMember(
"RemoveAll", BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance, null, listener, new object[] { false } );
Tuesday, 25 September 2007 19:40:26 (Jerusalem Standard Time, UTC+02:00)  #    -
Development
# Tuesday, 18 September 2007

It's actually pretty easy; get your web service up and running in axis, run svcutil.exe http://someserver/someurl/someservice?wsdl and you're good to go. Unless, of course, you're trying to return arrays from the Java side (primitive arrays at least, I didn't see much of a point testing with custom classes). Consider the following contract:

public interface ServiceInterface {
    int[] doSomething( String someParameter ); 
}

The generated WSDL looks something like this (edited for clarity... I hope):

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<wsdl:types> <schema xmlns="http://www.w3.org/2001/XMLSchema"> <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/> <complexType name="ArrayOf_xsd_int"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:int[]"/> </restriction> </complexContent> </complexType> </wsdl:types>
<wsdl:message name="doSomethingResponse"> <wsdl:part name="doSomethingReturn" type="impl:ArrayOf_xsd_int"/> </wsdl:message> <wsdl:message name="doSomethingRequest"> <wsdl:part name="someParameter" type="soapenc:string"/> </wsdl:message>
<wsdl:portType name="ServiceInterfaceService"> <wsdl:operation name="doSomething" parameterOrder="someParameter"> <wsdl:input message="impl:doSomethingRequest" name="doSomethingRequest"/> <wsdl:output message="impl:doSomethingResponse" name="doSomethingResponse"/> </wsdl:operation> </wsdl:portType>
 <!-- More uninteresting stuff -->



</
wsdl:definitions>

The WDSL is perfectly fine and is properly processed by Microsoft's svcutil.exe tool; the generated classes look and appear to function normally, but if you'll look at the deserialized array on the client (.NET/WCF) side you'll find that the array has been deserialized incorrectly, and all values in the array are 0. You'll have to manually look at the SOAP response returned by Axis to figure out what's wrong; here's a sample response (again, edited for clarity):

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/>
  <soapenv:Body>
    <doSomethingResponse>
      <doSomethingReturn>
        <doSomethingReturn href="#id0"/>
        <doSomethingReturn href="#id1"/>
        <doSomethingReturn href="#id2"/>
        <doSomethingReturn href="#id3"/>
        <doSomethingReturn href="#id4"/>
      </doSomethingReturn>
    </doSomethingResponse>
    <multiRef id="id4">5</multiRef>
    <multiRef id="id3">4</multiRef>
    <multiRef id="id2">3</multiRef>
    <multiRef id="id1">2</multiRef>
    <multiRef id="id0">1</multiRef>
  </soapenv:Body>
</soapenv:Envelope>

You'll notice that Axis does not generate values directly in the returned element, but instead references external elements for values. This might make sense when there are many references to relatively few discrete values, but whatever the case this is not properly handled by the WCF basicHttpBinding provider (and reportedly by gSOAP and classic .NET web references as well).

It took me a while to find a solution (after which I stumbled onto this post, which wasn't trivial to find): edit your Axis deployment's server-config.wsdd file and find the following parameter:

<parameter name="sendMultiRefs" value="true"/>

Change it to false, then redeploy via the command line, which looks (under Windows) something like this:

java -cp %AXISCLASSPATH% org.apache.axis.client.AdminClient server-config.wsdl

The web service's response should now be deserializable by your .NET client.

Tuesday, 18 September 2007 20:50:09 (Jerusalem Standard Time, UTC+02:00)  #    -
Development
Me!
Send mail to the author(s) Be afraid.
Archive
<2007 December>
SunMonTueWedThuFriSat
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345
All Content © 2017, Tomer Gabel
Based on the Business theme for dasBlog created by Christoph De Baene (delarou)