Sunday, November 17, 2013

CQ dispatcher vs Apache MultiViews

The short(er) version
The Apache MultiViews option in the <Directory> configuration will cause the request processing to end and return a 404 response instead of letting the request continue on to the remaining Apache modules in the processing chain such as mod_rewrite and mod_dispatcher.

This seems to happen when requesting a document that exists in a directory that is not yet on the filesystem and the MultiViews content negotiation code does not know what to do with the request.

The two instances I found this to occur in were requests for files without an extension, such as j_security_check, and paths that contain non-standard characters like a colon (:) such as those found in requests for CQ content stored beneath a jcr:content node. E.g.:
POST http://local.mysite.dev/content/mysite/en/login/j_security_check
GET http://local.mysite.dev/content/mysite/en/index/jcr:content/left/news.html
Some more background
So I encountered this maddening problem last week with the CQ dispatcher not quite working as it should have without an obvious reason.

The issue I was running into was that certain requests were returning a 404 response when the request passed through the dispatcher while the same request worked just fine when going directly to a publish instance (i.e. direct to the publish port such as 4503).

The problem turned out to be the Apache MultiViews option set on the <Directory> used by the virtualhost that served up my CQ website. I had Apache configured to pass all requests through to mod_dispatcher. In other words, no rewrites or proxies were configured at the Apache level.

For the most part everything worked great, cached as expected and all was well. But there were a handful of requests that were returning a 404 error for no apparent reason.

There were two requests in particular being plagued by these mystery 404's:
  • The website's login request
  • A request for data that was stored in the pages jcr:content
Illustration of the problem

A request that did work
Here's an example of a request that was passing through the dispatcher without problems and caching as expected.
Request URL: GET http://local.mysite.dev/content/mysite/en/login.html
Dispatcher Cache: /www/htdocs/content/mysite/en/login.html
Actual Results: request rendered and cached as expected
This one is a login page. Upon first request, when the dispatcher had not yet created a cached copy, the request passed through to mod_dispatcher, went to publish, rendered and was cached on the file system.

A request that was not working
Ok, so far so good. Everything seems to be working and the dispatcher is doing it's job. But now I submit the form and expect to see a request go to through the dispatcher into the publish instance.

Since it is a POST request I expect to see it go to publish every time.
Request URL: POST http://local.mysite.dev/content/mysite/en/login/j_security_check
Dispatcher Cache: N/A because it is a POST request
Actual Results: request returned 404 instead of passing through to the publish instance
But alas, I was not so fortunate. No traffic was registered in the publish instance and I get a 404 error in my browser. Hell, I get one even when I use cURL. The Apache request log did log the request so I know it go into the web server but the dispacher.log did not show any traffic. Hmmmm...

Of course when I went straight to the publish instance on port 4503 I everything worked just fine. I'm logged in and seeing my site. Something's up with my dispatcher configuration.

Another request that was not working
Thinking maybe something is weird with login requests I decided to try loading a different page in the website through the dispatcher to see if that worked.
Request URL: GET http://local.mysite.dev/content/mysite/en/index/jcr:content/left/news.html
Dispatcher Cache: /www/htdocs/content/mysite/en/lindex/jcr:content/left/news.html
Actual Results: request returned 404 instead of passing through to the publish instance, no file cached
This one had some news content that was a part of a news component on the page which stored its data under the page's node in the JCR. Most of the page loaded and cached just fine but that extra bit of content was also returning a 404.

Weird. This one was for an HTML document and I already had other HTML documents passing through and caching just fine. But this one was not working for some odd ball reason.

Finding the culprit
I spent a fair amount of time performing trial end error with different configurations but was unable to find a consistent rhyme or reason to the 404's I was seeing. Checking all the logs, I couldn't spot anything that was telling me what was wrong.

At least, not at first.

Hell, I could barely find anything in the logs for these requests beyond the Apache access_log registering the request. Nothing in the dispatcher.log, nothing in the publish logs. Weird.

Then I took a look at the Apache error_log and saw a log entry I'd not seen before (or at least never cared about before):
[Thu Nov 14 11:19:30 2013] [error] [client 127.0.0.1] Negotiation: discovered file(s) matching request: /www/htdocs/content/mysite/en/login (None could be negotiated)., referer: http://local.mysite.dev/content/mysite/en/login.html
Huh? Ok, well, maybe Google would have something to say about that error message. And as it turns out, it had a lot to say about it. The very first hit was a blog talking about a very similar problem:

http://www.bennadel.com/blog/2218-Negotiation-Discovered-File-s-Matching-Request-None-Could-Be-Negotiated.htm

Well, whaddya know? MultiViews eh? No idea why I'd need that. Let's see what happens when I remove it. So change this:
<Directory "/www/htdocs">
    Options Indexes FollowSymLinks MultiViews
    AllowOverride all
    Order allow,deny
    Allow from all
</Directory>
To this (MultiViews removed):
<Directory "/www/htdocs">
    Options Indexes FollowSymLinks
    AllowOverride all
    Order allow,deny
    Allow from all
</Directory>
Ah ha!
Of course once I removed that option my logins started working and news started showing up every time. In fact, it's been pretty smooth sailing with the dispatcher ever since.

And now it pretty much makes sense. The content negotiation processing turned on by the MultiViews directive really didn't like that missing directory.

I'm not sure why it didn't care about missing directories for that initial login.html request. Perhaps it understood based on the file extension that eventually one would be there at some point later on in the Apache request process chain. But when that extension does not exist or a funny character is present in the request path it sure looks like the content negotiation just gives up and goes home.

Frankly, I'm not really sure I care about the specifics on when the code that backs the MultiViews when and won't 404 early at this point. I'm just glad taking it out of the equation did the trick in this case (where I really do just want Apache to pass it on to the CQ dispatcher module thank you ;).

Conclusion
So far, I very much enjoy working with the CQ (now called AEM) CMS. I really dig the way it works and the tools it gives developers, authors, businesses and users. It has its moments, such as this, and could use some love in some areas. But overall it's a very cool platform.

This problem however was the kind that drives programmers to drink the hard stuff early in the day!

I'm not sure if I'm just lucky with the version of Apache I'm working with (the stock Apache on OS X 10.8). I'm just glad things are working the way they are supposed to now. Sometimes it just takes enough iterations of slamming your head into a concrete wall before you find and recognize the error message for what it is.

Hope this gives someone a chuckle or even helps save someone a bit of time.

Sunday, September 9, 2012

A trick to help with the Eclipse update blues

I think anyone who uses Eclipse has cussed at the software installer/updater/marketplace.  Tonight I tried to install the eGit plugin from the marketplace with no luck.  As usual, every time it tried to download something from download.eclipse.org, usually content.jar, it would hang until the 600s network timeout elapsed.

I did a little googling and stumbled upon a JVM setting that fixed the problem in this instance.  Crack open the eclipse.ini and throw this parameter into the -vmargs:

-Djava.net.preferIPv4Stack=true

After doing that the updates worked great.  All the network communication went by quickly on my cable modem and I wasn't sitting there wondering what the hell Eclipse is doing.  I can live with slow downloads when I can see progress going by but the 10 minute hangs with no feedback...

For what it's worth, the other trick I saw that people found helpful was to go to the 'Install New Software' screen and uncheck the 'Contact all update sites during install to find required software' check box.  That didn't help my issue, the JVM parameter is what did the trick there, but it's worth keeping in mind if updates are mis-behaving.

Any who, maybe this will help someone else out with Eclipse downloads.

Thursday, September 6, 2012

Using OpenMQ in Websphere (cliffnotes)

I couldn't find anything on using an OpenMQ message broker with the Websphere Application Server so here are the steps I followed.

Get the software
Download the developer edition of WebSphere
http://www.ibm.com/developerworks/downloads/ws/wasdevelopers/

* NOTE: on Ubuntu, replace the /bin/sh symlink that points to /bin/dash with a symlink that points to /bin/bash.  Otherwise the install will hang half-way through

Download the JavaEE SDK (probably called Glassfish) from Oracle

Setup OpenMQ
Start the imqbrokerd (the actual OpenMQ server)

Start the imqadmin tool, set up a queue, JNDI connection factory and destination.  This tutorial helps explain what to do:
http://docs.oracle.com/cd/E19909-01/817-3727/tutorial.html

Use this sample code to create a tester to make sure you have your queues all set up correctly:
http://www.java2s.com/Code/Java/J2EE/ThisexampleisasimpleJMSclientapplication.htm

Set up WAS
1. Create a new Generic JMS provider
Add these jars to the Classpath for the provider:

${glassfish3}/mq/lib/fscontext.jar
${glassfish3}/mq/lib/imq.jar
${glassfish3}/mq/lib/jms.jar
${glassfish3}/glassfish/modules/glassfish-naming.jar

Where ${glassfish3} is a variable that points to the glassfish3 directory on your system

If you forget one of these jars, werid werid werid exceptions occur that are hard to trouble shoot (catch them and print them to stdout when debugging)

External initial context factory:
com.sun.jndi.fscontext.RefFSContextFactory

External provider URL:
file:///tmp/OpenMQ

This should match the path used for the java.naming.provider.url in the OpenMQ object store

2. Create a Queue Connection factory

Name: lable is free form

JNDI name:
the JNDI name used by apps running in WAS, so this is the name WAS exposes for the resource

External JNDI name:
The JNDI name used in the OpenMQ object store for the ConnectionFactory resource which is listed under the "Lookup Name" column in the imqadmin tool

3. Create a Queue
Same instructions as the connection factory basically, only this time use the Destination in the OpenMQ object store instead of ConnectionFactory

And thats it!  If you're getting weird class cast execption or cannot connect exceptions, double check that all 4 jars are included AND that he external JNDI names matching.

WAS is terrible with it's Generic JMS exceptions. One trick is to catch and System.out.println the exception thrown when trying to pull the resource from JNDI or connect to the broker.  I found that this revealed more helpful exceptions that are not logged by WAS when the error occurs.