views:

1751

answers:

3

I have an EJB3 bean deployed as a web service in JBoss 4.2.2. In production the server is behind an Apache server that redirects requests to the Jboss server. This makes the WSDL have the wrong soap:address location. I was able to get the port and host name changed via the configuration file in server\default\deploy\jbossws.sar\jbossws.beans\META-INF, but I haven't been able to get it to switch the protocol to https.

The only way that I found was to specify my own WSDL (via here). By specifying https in the WSDL, JBoss picks up that it is https. However, although that is fine in production, in QA https is not used (and the service that connects to it requires valid https, so self-signed certificates won't do). So while I could get a real certificate for QA (fake certificate authorities won't do), I would rather have it be http in QA. Is there a way to force JBoss to change the protocol or otherwise change the address so that it uses http in the soap:address?

EDIT: It seems like this issue was brought up recently in their bug database and was rejected. That would imply to me that they have a work around. But what is it?

Further EDIT: At this point, I know that an EJB3 Interceptor doesn't work (they are not activated at all) and SOAPHandler's don't intercept the call to retrieve the WSDL (tested - they pick up everything else). So the filter idea is intriguing, but it is far from clear where to put it.

The URL as shown by JBoss is:

Endpoint Name jboss.ws:context=QuickBooks-QuickBooksWebService,endpoint=QBWSBeanEJB Endpoint Address https://127.0.0.1%3A8443/QuickBooks-QuickBooksWebService/QBWSBeanEJB?wsdl

(Note that is when I use the custom WSDL to force https but JBoss is configured to rewrite it).

The version of JBossWS I'm using is what is bundled with 4.2.2, which according to this is 2.0.1

EDIT: With regards to the rewriting, this was indeed attempted. Here is what I found. I could get it to rewrite the host (or not as needed) and the port, but only for the recognized protocol. So in order to get it emit an https, I had to configure the bean's transport guarantee to CONFIDENTIAL, and enable https on the JBoss server, then all requests were redirected to https within JBoss. I didn't test if this would even work with mod_jk (does the AJP protocol still work if CONFIDENTIAL is required, and will the WSDL get the right protocol if the request comes over AJP? I didn't test it), however doing that has the same net effect - the requests must go over https. There is no way to have the request come in over http or AJP and then have it emit a soap:address as https, specifically configured on a per-server basis (in QA and dev, http, in but in production https even though the ssl was terminated by Apache). Using the custom WSDL got me closer, in that the request is coming in on HTTP, but the soap:address says https. Perfect for production (using the rewrite feature to push the port to 443 instead of 8443), but useless for QA (dev I don't care about, because the dev build could be different and create a different jar if needed, but I'm not comfortable with QA and production having different build processes if I can avoid it).

A: 

We've faced similar issues in the past and decided that using SSL in all QA environments was desired (there are downsides - especially when debugging the HTTP streams - but what doesn't have downsides?)

This can be easier that you may think however since you seem to have control of all environments involved: you basically just need to tell Java that your self-signed certificates are "good". To do this you need to add yourself as a certificate authority in Java.

A good overview of the process is here:

http://en.wikibooks.org/wiki/WebObjects/Web_Services/How_to_Trust_Any_SSL_Certificate

Here's information from Sun about the KeyStore tool (although you should look for the reference specific to your version/JVM as there are differences):

http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html

Once this is done your self-signed certs in QA will be 100% "valid" in Java.

You can even save some money sometimes in Prod: if you control both sides of the environment you can do self-issued certs in Prod and save some cash. Heck, if your user community is small/controlled enough you can add your self-signed authorities directly in client browsers as well (although that seems like it would be more trouble that its worth).

Hope this gets you moving in a useful direction!

Jim Davis
Hi Jim, Thanks for the answer. Unfortunately, we don't control the web service client very well (it is a third party application, QuickBooks Web Connector) and using self-signed certificates isn't a great option. Although doable, it would be much better to get JBoss to connect over http or https depending on configuration, not on the code of the webservice componenent.
Yishai
+1  A: 

Why don't you set up a servlet filter on your web application which watches for WSDL requests and rewrites the soap:address in the responses using values from its configuration? Then you can set it up however you want.

It may not be the most elegant solution, I agree :-) - but then that would have been for the configurability to be in JBoss, wouldn't it? And if JBoss eventually introduces this feature, you can just remove the filter.

Update: A typical JBoss EAR has the following structure:

myapp.ear
|+ META-INF
   |+ applications.xml and jboss-app.xml
|+ myapp.war
   |+ web pages and JSP /JSF pages
   |+ WEB-INF
      |+ web.xml, jboss-web.xml, faces-config.xml etc.
      |+ lib
         |+ tag library JARs
      |+ classes
         |+ servlets and other classes used by web pages
|+ myapp.jar
   |+ EJB3 bean classes
   |+ META-INF
      |+ ejb-jar.xml and persistence.xml
|+ lib
   |+ Library JARs for the EAR

Can you confirm that your deployed EAR has this structure? If not, how is it different?

Update #2: JBoss automatically generates a web.xml for EJB3 beans which are in a JAR inside the EAR being deployed. This web.xml is definitely hard-coded (i.e. doesn't use as a basis an XML file which you can tweak) and so it is awkward (though not impossible) to change this to include a filter (you basically need to change various things in jboss-beans.xml to point to your own web.xml generator, which can do whatever you want). It would be a lot less development work if the EJB3 beans were in the WAR file - then you can configure the Filter conventionally using the WAR's web.xml, which you presumably have control over. However, that might be problematic if that's a third-party JAR you're using - or it might not. Without knowing the details it's hard to see which the best approach will be.


Edit from the question's author:

Thanks for all the effort. This appears to be the basic approach - intercept and change the WSDL. If you can't make your webservice a POJO assigned in the war, where you can use a servlet filter, then you can make a servlet that proxies the request, and do it that way. That was my solution.

Vinay Sajip
That is not a bad idea, thanks! The thing is that this is deployed as an EJB3, not inside a WAR. Of course that could be changed, but that would be somewhat involved.
Yishai
Well, so far I've discovered that the relevant location for the filter is here. Any further help in developing it (including class locations to get around classloader issues) appreciated. http://www.jboss.org/community/wiki/WSDLPortFixFilter
Yishai
You can ignore my previous comment, that location isn't valid in 4.2.x
Yishai
Confirmed, that is my ear structure.
Yishai
I set the CONFIDENTIAL via annotation.
Yishai
Thanks for all the work. It would have involved a lot of rework to the bean to do that, since it relies on some transactions and JPA to do some of its job. I found a somewhat different workaround, but also based on your filter idea, which I'll post soon.
Yishai
A: 

Basically the only solution I found to this was to create a servlet which intercepts and proxies the WSDL request. I copied and implemented ProxyServlet from Apache's pivot project (it took some not-insubstantial changes, such as the Apache implementation only wrote responses to gets not posts). Basically the implementation checks if the request is a request for a WSDL, and changes the soap:address property in the WSDL. Hardly nice, but, it worked around that problem, but the whole thing is still not working because after we have communication, there is a further problem. Another question on SO I guess.

Yishai
Thanks for the bounty, and sorry to hear you're still having trouble. It is basically the same principle as the filter (intercept and change outgoing XML) but it's a shame a more elegant solution couldn't be found.
Vinay Sajip