March 2008

« April 2008 | Main | January 2008 »

Using Maven and CVS on Windows

Monday, March 17, 2008

I am currently using Maven with CVS on a Microsoft Windows environment and had a minor issue with the Maven changlog plugin. This plugin builds a report from your CVS, or other SCM, repository that displays a list of changes made in the repository.

The issue I had was that this plugin ran fine on the Unix build box but not on my local development station that was running Windows. While not a pressing issue, it was annoying because I could not run the mvn site command locally to test any site changes out. Every time I ran the command I would get an exception about no credential being found. I had run the cvs login command previously but the plugin stubbornly refused to work even though using CVS directly at the command line worked fine. Unfortunately the client does not allow an anonymous user so the only way to get this working is to authenticate successfully.

Turns out the issue is that on Windows I was using the cvsnt binaries and there is one significant difference between CVSNT and CVS with respect to cvs login. Under CVS, when you run the cvs login command, it generates a .cvspass file that is placed in your user directory. The CVSNT binaries though place the credential in the registry. The Maven SCM plugins work with CVS directly and do not use binaries so it expects the standard CVS behavior of using the .cvspass file.

The Maven 1.x changelog plugin did have a way to generate this .cvspass file but this goal does not exist in 2.x. Therefore to generate this file I simply created a quick and dirty ANT build.xml that runs the cvspass task. Here is the build file I created:

<project name="cvspass" default="cvspass">
  <target name="cvspass">
    <cvspass cvsroot=":pserver:${username}@x.x.x.x/apps/cvs/cvsroot"
      password="${password}"
    />
  </target>
</project>

The username and password are picked up from the command line when this task is run. Being lazy, I simply run a setDomainEnv.cmd in a WebLogic domain I have lying around to initialize ANT. Afterwards, to run the ANT task simply execute the following at a command line:

ant cvspass -Dusername=xxxxx -Dpassword=xxxxx

With the .cvspass in place the mvn site command now works fine in all locations.

Posted by Gerald Nunn at 4:01 PM | Categories: Java | Permalink |


Yet even more on the WLP ResourceProxyServlet

Friday, March 14, 2008

While I have blogged about the ResourceProxyServlet a couple of times, a little recap never hurts to set the stage. As many of you know, the ResourceProxyServlet is used in WSRP to proxy resources on the producer through the consumer to the browser. Essentially it provides a means for the browser to see resources on the producer even if the producer is behind a firewall by using the consumer a proxy.

Recently the client I was working with was having an issue where requests in a federated portal were taking a long time to complete and I thought it would be useful to talk about the experience to aid others who may encounter a similar problem. Using FireBug it was easy to see that the resource requests were timing out for some reason. Looking at the log we could see the following exception occurring a short time after the request:

java.net.ConnectException: Tried all: '1' addresses, but could not connect over HTTP to server: 'xxx.xxx.xxx.xxx', port: 'xxxxx'
        at weblogic.net.http.HttpClient.openServer(HttpClient.java:361)
        at weblogic.net.http.HttpClient.openServer(HttpClient.java:430)
        at weblogic.net.http.HttpClient.<init>(HttpClient.java:159)
        at weblogic.net.http.HttpClient.<init>(HttpClient.java:149)
        at weblogic.net.http.HttpClient.New(HttpClient.java:265)
        at weblogic.net.http.HttpURLConnection.connect(HttpURLConnection.java:170)
        at com.bea.wsrp.util.CompatWsdlParser.getWsdlDocument(CompatWsdlParser.java:309)
        at com.bea.wsrp.util.CompatWsdlParser.getWsrpPortUrls(CompatWsdlParser.java:64)
        at com.bea.wsrp.wsdl.FixupWsdlParser.tryFixup(FixupWsdlParser.java:74)
        at com.bea.wsrp.wsdl.FixupWsdlParser.parse(FixupWsdlParser.java:63)
        at com.bea.wsrp.wsdl.WsdlInfoImpl.<init>(WsdlInfoImpl.java:169)
        at com.bea.wsrp.wsdl.GlobalWsdlPool.getWsdlInfo(GlobalWsdlPool.java:55)
        at com.bea.wsrp.consumer.resource.DefaultResourceConnectionFilter.getMarkupPortUrl(DefaultResourceConnectionFilter.java:79)
        at com.bea.wsrp.consumer.resource.DefaultResourceConnectionFilter.allowedURL(DefaultResourceConnectionFilter.java:37)
        at com.bea.wsrp.consumer.resource.ResourceProxyServlet.internalService(ResourceProxyServlet.java:219)
        at com.bea.wsrp.consumer.resource.ResourceProxyServlet.doGet(ResourceProxyServlet.java:157)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
        at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:226)
        at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:124)
        at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:283)
        at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
        at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
        at com.bea.portal.tools.servlet.http.HttpContextFilter.doFilter(HttpContextFilter.java:60)
        at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
        at com.bea.p13n.servlets.PortalServletFilter.doFilter(PortalServletFilter.java:315)
        at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)
        at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3368)
        at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
        at weblogic.security.service.SecurityManager.runAs(Unknown Source)
        at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2117)
        at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2023)
        at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1359)
        at weblogic.work.ExecuteThread.execute(ExecuteThread.java:200)
        at weblogic.work.ExecuteThread.run(ExecuteThread.java:172)

Looking at the stack trace above, the issue appeared to be a connection hanging when the DefaultResourceConnectionFilter tried to get the WSDL from a producer. The obvious question was why was this class attempting to get a producer WSDL? Using JAD, a Java decompiler, along with Jadclipse it was easy to see what was happening by looking at the code.

In WLP 8.1, a connection filter was the primary way to secure the ResourceProxyServlet to ensure that it only accessed specific content. Otherwise the proxy servlet becomes a security hole since it could be used to access arbitrary resources from any machine the consumer had access to simply by manipulating the wsrp-url parameter in the resource request. You can view the original 8.1 documentation at Creating a Resource Connection Filter. In WLP 10 this feature is still available but somewhat deprecated in favor of the WebLogic Server connection filter.

Having said that, a default connection filter is still used with the resource proxy servlet and what it does is to ensure that the requested resource IP address matches an IP address for one of the producers markup address. This was where the WSDL request was coming from, the filter needs the markup address from the WSDL to do this comparison. The problem the client had was that the application's wsrp-producer-registry.xml had a dead producer in it, while none of the proxy portlets referenced it the default connection filter still used it to check resource requests. When it tried to get the WSDL for the dead producer the connection hung until a TCP timeout occurred since the IP referenced was not available on the test network.

So now that issue was out of the way, the client tried accessing the resource again and was finding that the resource request returned immediately but with a 401-Forbidden status code. Looking at the logs again we saw the following error being logged on the producer:

<Mar x, 2008 x:xx:xx PM EST> <Warning> <WSRP-Consumer> <BEA-420756> <[/xxxxxx] Resource http://xxx.xxx.xxx.xxx:xxxxx/xxxxx/javascript/xxxxxxxx.js requested by client from xxx.xxx.xxx.xxx not served.>

This is a message returned by the DefaultResourceConnectionFilter when it refuses to serve a request. Looking at the IP requested it matched the IP registered for the producer's WSDL so it was not immediately obvious what the issue was. However after looking at the code for the DefaultResourceConnectionFilter class, it showed that it was using the markup address and not the WSDL address for doing the comparison. When you access a producer WSDL you will see that it contains a variety of IP addresses in bindings for getting the markup, the service description and others which are created by the producer. These addresses are the ones actually used by the underlying WSRP implementation to interact with the remote portlet, not the WSDL address.

The customer had a clustered environment for the producer but did not have the front-end host parameters set, thus these bindings contained the IP and port of a server in the cluster rather then the appliance that was fronting the cluster. As a result the address for the resource, which was the front end appliance, did not match the server's address. The solution here was an easy one, setting the front-end host parameters for the producer cluster in the WebLogic console corrected the issue in that the generated WSDL service addresses now used the front end host settings. As a result the resource started being served correctly.

So there you have it, a few interesting tidbits learned about the ResourceProxyServlet.

Posted by Gerald Nunn at 4:01 PM | Categories: WebLogic | Permalink |