Matthew Woodward // * CFML, Grails, and Java Developer
* Principal IT Specialist, US Senate
* Open BlueDragon Steering Committee Member
* All-Around Geek
<Proxy balancer://tc> BalancerMember http://ip.address.here:port route=tc01-uniqueID BalancerMember http://ip.address.here:port route=tc02-uniqueID </Proxy> ProxyPass /cluster-test balancer://tc/cluster-test stickysession=JSESSIONID:jessionid ProxyPassReverse /cluster-test balancer://tc/cluster-testDebugging Clusters
Starting httpd: httpd: apr_sockaddr_info_get() failed for host.name.here httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerNameThe second part of that error is pretty common (and nothing to worry about most of the time), but I hadn't seen that first error message before. A bit of scroogling and experimenting led to the solution quickly enough so this is another one of those "so I don't forget" blog posts more than anything, but hopefully it'll help someone else who runs into this. In my case the server's host name didn't have a corresponding DNS entry yet, so to resolve the error I simply added the server's host name to /etc/hosts and pointed it to 127.0.0.1.Of course depending on your situation you may want a DNS entry too, but I tend to have all the host names for the server itself in the hosts file on the server so it doesn't have to do a DNS lookup only to find itself. Particularly when you're behind a load balancer things get weird if you don't leverage your hosts file.
This came up a couple of times on mailing lists recently, and since it's something I sometimes forget to do when I set up new Windows servers with Apache, I figured I'd document it here.
If you're running into stability problems with Apache on Windows and can't switch to Linux, doing the following seems to help quite a bit. First, find these lines in your httpd.conf file and uncomment them:EnableMMAP off EnableSendfile offThen right below those lines, add this line:
Win32DisableAcceptExParticularly if you're seeing errors along the lines of "The specified network name is no longer available. : winnt_accept: Asynchronous AcceptEx failed" or "The semaphore timeout period has expired. : winnt_accept: Asynchronous AcceptEx failed" that last line should eliminate those errors.
If you're interested in learning more about what's behind these errors, there's a nice post about it on the "My Digital Life" blog.
A few quick tips if you find yourself having to access a network drive from Apache and Tomcat on a Windows Server. This is all pretty much old hat but since I still get questions about this fairly regularly and was just setting up some things on some servers this weekend, I figured I'd write this up.
In my situation we're running an application on three VMs behind a load balancer, and users can upload files from the application. Since I didn't want to set up a process behind the scenes that copied uploaded files between all three servers (though it would have given me a chance to take another run at using Unison), we have an uploads directory living on a SAN. This way no matter which VM the user is on, the uploads all go to and are served from a single file system.
On Windows Server, by default services run under the Local System Account, which doesn't have access to network resources. So if you install Apache and Tomcat as services and expect to be able to point them to a UNC path for some of your content, that won't work out of the box. You need to be running Apache and Tomcat under an account that has network access. In most environments in which I've worked this type of account is typically called a "service account," because you'll end up getting a domain account just like your typical Active Directory user account, but of course it won't be associated with a human being.
Once you have that account in place, you go into your services panel, right click on the service, click on "Properties," and then click the "Log On" tab. You'll see by default the Local System Account radio button will be checked. Click the radio button next to "This Account," enter your service account information, click "OK," and then restart the service. At this point your service will be running under the service account and will have access to network resources. Note that you'll have to do this for each service that needs access to the network drive, which in my case meant doing this for both Apache and Tomcat.
That takes care of the web server and things at the Tomcat level in terms of basic access, but you'll likely be configuring an alias of some sort to point to the network drive. In my case I wanted /uploads to point to \\server\sharename\uploads, which meant setting up an alias in Apache, a Context in Tomcat, and a mapping in OpenBD. This is where a lot of people get confused, so I'll go through each of these settings one by one.
The necessity for a web server alias is probably pretty obvious. If you're serving an image directly from your web server, e.g. http://server/uploads/image.gif, if /uploads doesn't exist under your virtual host's docroot, then Apache will throw a 404.
Allowing Apache to access the network drive involves (depending on how you have Apache configured) using a Directory directive to give Apache permission to access the directory, and then an Alias directive so Apache knows where to go when someone requests something under /uploads. So the following goes in your virtual host configuration file:
<Directory "\\server\sharename\uploads"> Order allow,deny Allow from all </Directory> Alias /uploads "\\server\sharename\uploads"
You may have other stuff in your Directory directive as well but that's the basics of what will allow Apache to see /uploads as the appropriate location on your SAN.
The next layer down will be your CFML engine. Remember that if in your CFML code you want to read or write files to /uploads, even though Apache knows what that is now, your CFML engine will not. I'm emphasizing that point because it's such a common source of confusion for people. If things are happening in your CFML code, it won't be interacting with Apache at all, so it won't know about the Alias you set up in Apache. Simple enough to solve with a mapping; just go into your CFML engine's admin console and create a mapping that points /uploads to \\server\sharename\uploads and that handles things at the CFML level.
Lastly comes Tomcat. Depending on how you're serving files, you may be proxying from Apache to Tomcat, so if Tomcat needs to know where /uploads lives, since it's not in the webapp's base directory Tomcat will throw a 404 unless you tell it where /uploads is located.
Tomcat doesn't have Aliases in the same way Apache does, but what you can do in Tomcat is configure multiple Contexts under a single host. So in Tomcat's server.xml (or in a separate host config file if you prefer), you simply add a Context that points /uploads to the network location:
<Host name="myapp"> <Context path="" docBase="C:/location/of/my/app" /> <Context path="/uploads" docBase="\\server\sharename\uploads" /> </Host>
So now you have things set up in such a way that Apache, your CFML engine, and Tomcat all know where /uploads really lives.
Another point of confusion for people on Windows is the concept of "mapped drive" letters. A lot of people think that if you map \\server\sharename to a drive letter of let's say D:, than in your code you can then access the uploads directory we've been using as our example via D:\uploads.
The simplest way to explain why this doesn't work is to point out that mapped drive letters are associated with a Windows user account. They don't exist at the operating system level. While you may remote into the server using your credentials, map to a network location, and assign that to drive letter D:, another user logging in won't see that mapping, and services running on the server under various user accounts definitely won't know anything about mapped drives.
This is why in all the examples above you see the full UNC path to the network resource being used. You have to use the UNC path in order to get this all to work correctly because that's the only way services running under a service account will be able to address the network resource.
Hope this helps eliminate some of the persistent confusion I see around this issue.
I'm setting up some new servers with some semi-complex URL rewriting and proxying going on of both the HTTP and AJP varieties. For this particular application the home page is actually a completely separate application that's running as its own webapp on Tomcat, and everything else runs from within the webapp itself. (I'll spare you the details; just assume I'm not insane and this was the best way to handle this situation.)
So for all this to work properly I have the following rewrite rule:RewriteRule ^/$ http://otherapp/ [NC,P] ProxyPassReverse / http://otherapp/Then below that I have a bunch of AJP rewrites for the webapp itself. What was throwing me fits was that everything was working great except for this one rewrite rule, which would attempt to load indefinitely until I killed the request. I stared at my virtual host config file for quite a while and finally paused on this line:
ProxyPreserveHost OnGiven that the ^/$ rewrite rule is proxying out to a different host, it seems kind of obvious now that of course that would confuse Apache. Removing that line and adding some aliases for the other host's CSS and images directory did the trick. Maybe no one else will be in this exact situation, but if you're ever faced with a blank white screen while waiting forever for an HTTP proxy to go through, this tip might come in handy.
RewriteRule ^/([a-zA-Z0-9-]{1,})/(.*)$ ajp://%{HTTP_HOST}:8009/$1/index.cfm/$2 [NE,P]
Translation:
ProxyRequests Off
<Proxy *>
Order deny,allow
Allow from 127.0.0.1
</Proxy>
ProxyPreserveHost On
ProxyPassReverse / ajp://your-host-here:8009/
RewriteEngine On
# if it's a cfml request, proxy to tomcat
RewriteRule ^(.+\.cf[cm])(/.*)?$ ajp://%{HTTP_HOST}:8009$1$2 [P]
# if it's a trailing slash and a real directory, append index.cfm and proxy
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^(.+/)$ ajp://%{HTTP_HOST}:8009%{REQUEST_URI}index.cfm [P]
# if it's a real file and haven't proxied, just serve it
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f
RewriteRule . - [L]
# require trailing slash at this point if otherwise valid CMS url
RewriteRule ^([a-zA-Z0-9/-]+[^/])$ $1/ [R=301,L]
# valid cms url path is proxied to tomcat
# MUST COME AFTER ANY OTHER FIXED/EXPECTED REWRITES!
RewriteRule ^/([a-zA-Z0-9-]{1,})/(.*)$ ajp://%{HTTP_HOST}:8009/$1/index.cfm/$2 [NE,P]
Note that you can put the rewrite rules in a .htaccess file or in the virtual host configuration file. I prefer having the rewrite rules be defined as part of the virtual host, so in my case I have all this in the <VirtualHost> block in my virtual hosts config file. (I have a bunch of other stuff in the virtual host configuration as well but I left everything other than the parts relevant to the URL rewriting out to avoid any confusion.) That's it! I hope that helps others looking into doing this with Mura, or that you at least learned a few things about mod_rewrite along the way.
# This awk script takes a Microsoft IIS Web server log file in the default format # and converts it into NCSA common format.
Very handy and very fast. I used this to convert some IIS logs for load testing since JMeter doesn't like the default IIS log format.
LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule rewrite_module modules/mod_rewrite.soNext, if you're doing CFML stuff you'll want to add index.cfm as a DirectoryIndex, so find this section and update accordingly:
<IfModule dir_module> DirectoryIndex index.cfm index.html </IfModule>You can have as many directory indexes as you want, just separate with a space and realize they will get hit in the order in which they're declared. Finally, you'll want to enable name-based virtual hosting so you can have multiple virtual hosts sharing the same IP address. Towards the bottom of httpd.conf, find this section and uncomment the Include directive that will load the virtual hosts configuration file. When you're done it should look like this:
# Virtual hosts Include conf/extra/httpd-vhosts.confSave httpd.conf, and now let's take a look at how to configure your virtual hosts.Virtual Host ConfigurationOpen up conf/extra/httpd-vhosts.conf so we can configure some virtual hosts. You'll be spending a lot of time in this file as you use Apache. First, make sure this line right after the big comment block at the top is uncommented:
NameVirtualHost *:80This enables name-based virtual hosts for all IP addresses on port 80. Next you'll see a couple of examples of virtual hosts. You can either delete those or comment them out by putting a # on each line. I tend to leave them in there but comment them out for reference. For your first virtual host, let's set one up for localhost because (at least in my experience) once you enable name-based virtual hosting, you have to have a virtual host even for localhost. Add the following section, adjusting the DocumentRoot as needed based on where you installed Apache:
<VirtualHost *:80> ServerName localhost DocumentRoot "C:/Program Files (x86)/Apache Software Foundation/Apache2.2/htdocs" </VirtualHost>Save the file, and then restart Apache just to make sure all the changes we've made are working. If Apache doesn't restart don't panic, that just means you have a syntax error somewhere. Double-check everything and try again. If you can hit localhost in your browser and see "It works!", well, that message says it all I guess. Note that if you have multiple IP addresses on your machine and want to tell a virtual host to use a specific IP, or if you want to run a site on a port other than 80, you can replace the * with an IP, and the 80 with whatever port you need. Next let's configure a more real-world virtual host. I'll be using foo.com as my example, and we'll want people to be able to hit the site using foo.com or www.foo.com. I'm also going to tell Apache to use a log file specific to this site to make diagnosing problems and doing reporting easier. There are a few other things in here that I'll explain in a moment.
<VirtualHost *:80> ServerName foo.com ServerAlias www.foo.com DocumentRoot "C:/path/to/foo" Alias /CFIDE C:/path/to/CFIDE <Directory "D:/path/to/foo"> Order allow,deny Allow from all </Directory> CustomLog "logs/foo-access.log" common </VirtualHost>The ServerName and ServerAlias information is pretty self-explanatory--foo.com is the primary name for this virtual host, but with the alias of www.foo.com, either foo.com or www.foo.com will hit this virtual host. DocumentRoot tells Apache where to find the files that it will be serving when someone hits this virtual host.I threw an Alias in the mix simply to show how "virtual directories" (in IIS speak) work. Let's say in this case I want foo.com to have access to my CF administrator or maybe the javascript files that are stored in the CFIDE directory, but that CFIDE directory is not inside this host's docroot. The Alias directive tells Apache that when someone is requesting /CFIDE on this virtual host, those files will actually be served from somewhere outside the virtual host's docroot. The <Directory> directive requires a bit of explanation. For security reasons, by default all directory access (other than the default localhost site) is denied by Apache. This is done in the main httpd.conf file, so you can either make the change there, or I prefer to do this on a case-by-case basis inside each virtual host. In the case of a public site you won't know where people are coming from so you have to tell Apache to allow access to that directory from anywhere, which is done with the "Allow from all" line. I left this out, but note that you will likely have to add a <Directory> entry for the C:/path/to/CFIDE directory as well. Finally, I tell Apache to create an access log specific to this site instead of using the global Apache logs.For a lot of virtual hosts that's literally all there is to it. But since what started this whole process was rewriting issues, let's take a look at some of the cool things you can accomplish (and shoot yourself with) by using mod_rewrite. URL Rewriting and ProxyingFor the app in question we do a lot of URL rewriting and proxying so we can give the users a single site that actually is comprised of multiple sites, potentially on different physical servers. This is also a great way to handle long-term migrations where you have a legacy server that you don't really want to touch but still need content from, and you want to add a newer server in the mix. As with everything else related to Apache this is powerful stuff, but the basics are relatively simple. I do love this quote from the mod_rewrite docs, however: "The great thing about mod_rewrite is it gives you all the configurability and flexibility of Sendmail. The downside to mod_rewrite is that it gives you all the configurability and flexibility of Sendmail.''Let's start with a basic rewrite rule, and then we'll look at what I have to do a lot of which is proxying. Let's say for whatever reason in the foo.com virtual host you want requests to foo.html to actually hit bar.html. First we need to enable the rewrite engine in our virtual host, so inside your <VirtualHost> block, add this line:
RewriteEngine onNext we add a simple RewriteRule to tell requests for foo.html to be rewritten to bar.html:
RewriteRule /foo.html /bar.html [NC]The [NC] bit at the end stands for "no case," so that way both foo.html and FOO.HTML will be rewritten to bar.html. There are a ton of flags to do various things outlined in the docs, and if you want some nice rewrite example examples they have those too. So far so good? Next let's tackle proxying. Instead of a simple rewrite from foo.html to bar.html, let's say you want everything under a particular directory to be proxied to another server. To make the example more concrete, let's say your company has an intranet on one server and an employee directory that runs on another server, but you want people to be able to access the employee directory directly from your intranet. If you wanted to do a simple redirect from http://intranet/empdirectory to http://empdirectory, that's simple enough:
RewriteRule ^/empdirectory(.*) http://empdirectory$1 [NC,R]The (.*) after /empdirectory will include anything that comes after /empdirectory, and this is tacked onto the end of the remote URL via the $1. The "R" flag tells Apache to do a redirect for this RewriteRule, and you can even set the status code for the redirect. This does change the URL in the user's browser, however, so what if you didn't want that to happen? This is where proxying comes in. First, we change the "R" flag to a "P":
RewriteRule ^/empdirectory(.*) http://empdirectory$1 [NC,P]Now we're proxying instead of doing a redirect (and note that mod_proxy needs to be enabled to use the P flag, which is why we did that earlier), but if this is all you do you'll notice that the URL in the browser still changes. This is because there's nothing in place to handle proxying the response back to the requestor. So we need to add a ProxyPassReverse directive, which will allow us to hit http://intranet/empdirectory and keep that URL while the content is actually served from http://empdirectory.
RewriteRule ^/empdirectory(.*) http://empdirectory$1 [NC,P] ProxyPassReverse /empdirectory http://empdirectoryWith all this in place you can serve content from another server without your users knowing they're hitting another server. There are about a million and one other things you can do with mod_rewrite, but my only intent with this post was to share what I had to do in my specific move from IIS to Apache in the hopes it might help others who want to make this move. ConclusionEven though it was under duress, I'm honestly glad ISAPI Rewrite totally failed since that led me to setting up Apache on this box. After seeing ISAPI Rewrite have its various meltdowns I simply would not have felt comfortable using it. I'm sure I could have contacted support and gotten things figured out eventually, but it took me far longer to write this blog post than it did to switch to Apache, particularly since the rewrite syntax of ISAPI Rewrite is largely compatible with Apache's. I'm going to sleep much better at night knowing Apache is powering this app instead of being constantly worried that ISAPI Rewrite will have another meltdown. I should have made this disclaimer at the beginning but I am in no way an Apache expert, so if there are different or better ways to do any of this, if anything is explained poorly or incorrectly, or if I omitted any important details, please comment.
File this one under "you can't win 'em all," or "hell has frozen over," or take your pick (though I refuse to file this under "hat eating" because it isn't a change of opinion, it's just a necessity in this case). Since I'm having to go through and set up Apache 2.2, Tomcat 6.0, and Open BlueDragon on a Windows 2003 Server, I figured I'd make lemonade and write up a step-by-step for those of you stuck in the same situation. So here's how to install all this very cool stuff on a very uncool operating system.
(If you prefer to read this in Google Docs, here's the link.)
All these steps should also work on Windows XP or Vista, which is handy for development purposes if you're on a Windows box, but again I can't really verify. If you're using XP I strongly, strongly encourage you to at a minimum install Apache as your web server. The version of IIS that comes with XP only allows you to use one web site, which for web developers simply isn't acceptable and in my opinion shields people from how things actually work in the real world. Sermon over.Mac users--you will probably be able to follow along nearly line-by-line on this as well. It seems that the Mac install of Apache is nearly identical to the one on Windows (Apache on Linux is a bit of a different beast), and by default all the proxying stuff we're going to be using is enabled on the Mac. Not sure that's the greatest idea, but that's the way it seems to be on the Mac.Before digging in, note that there's a LOT going on here, and that you'll reach a couple of points along the way where you may think, "It's working! I don't need to read the rest!" And if that particular setup works for you, that may be true. Realize, however, that even though later steps may supersede earlier ones, this is by design to give people a bit of a crash course in the various options involved and how they all work together. And you might want to read to the end to change a couple of settings so your server doesn't crash all the time. Seriously.Also note that if you're here to learn how to use Tomcat and OpenBD with IIS, I can't help you. I have to draw the line somewhere. In all seriousness though, if someone has tips on getting IIS hooked into Tomcat I'd love to point people to it; I just don't personally have the experience to help people with that and won't be digging into IIS in this sort of setup. Ever. If you absolutely MUST embark on this fool's errand, here's some instructions that may help. Break a leg (or your registry).So grab a cup of tea, coffee, beer, curare, or whatever floats your boat and let's do this thing.Installing Apache 2.2
With Apache downloaded, run the installer, entering the appropriate information as you go. Note that for the rest of this how-to guide I'm assuming you installed Apache at C:\Program Files\Apache Software Foundation\Apache2.2, which is the default. I'll refer to this using {APACHE_HOME} for short, but where the full path is specified, if you installed Apache elsewhere you'll have to make the necessary adjustments.
If you don't see that, uninstall and give it another shot. Next we'll need Java.
Note that since Tomcat runs on Java you can actually grab any download you want and it will work, but the Windows Service Installer is what you'll want on a production Windows server so Tomcat is installed, as the name implies, as a Windows service. This also gives you a little monitoring tool that lets you click buttons to stop and start Tomcat as opposed to having to--the horror--type a command in a terminal.
After the download, run the installer and again you can accept the defaults for the most part. During the installation process you will provide Tomcat with a user name and password. This is used to access the Tomcat Manager, and we'll cover that in a moment.The installation will also prompt you for the location of Java on your machine. Although based on what I read Tomcat 6 can run on either a JRE or a JDK, personally I point Tomcat to the JDK as opposed to a JRE. This is perhaps out of habit since older versions required a JDK, but if you do use the JRE realize you may need to set slightly different variables if you start messing with your settings, specifically JRE_HOME instead of JAVA_HOME. Unless you have a reason not to, I'd recommend pointing to your JDK directory, which you downloaded and installed in the step above.When the install is done you likely will want to go into your services panel and set Tomcat to start automatically when the machine boots. By default it's set to manual startup.If everything went well and you didn't change the default port from 8080, which I don't recommend doing unless you really need to, you can now browse to http://localhost:8080 and see this:
As with the previous steps if something went awry, uninstall and give it another shot. In my experience this all does go quite smoothly though.
It ain't fancy, but it does let you see what applications are deployed, active sessions, and some other good information. You can also reload individual web applications from the manager, which is handy if one app needs to be reloaded due to code changes or problems and you don't want to impact all the other applications on Tomcat.
So far so good? Excellent! (You did answer in the affirmative, correct?) Let's get our OpenBD on.Installing OpenBDAs I said above, OpenBD and all CFML engines are Java web applications, so with Tomcat up and running the "install" of OpenBD is really just a deployment of the OpenBD Java web application, or WAR (which stands for Web ARchive. Yeah, I know--slick acronym eh?).Much as people complain about the complexities of Java, deployment is one thing Java got 100% right in my opinion. WAR files are actually ZIP files that contain a deployment descriptor, which means any Java server that adheres to the Java standards (and they all do) will understand what a WAR file is and what to do with it. There are differences in how the specific Java servlet container or JEE server will handle the WAR, but we won't dig into that here. Just know that a WAR file is a standard in the Java world, and it means dead-simple deployment. Copy a single file to the right place and your application is deployed.So where do we get this WAR file? Why from the OpenBD download page of course!
You can grab the latest stable (1.0.1 as of this writing) or the nightly build; either will work fine. Just make sure to grab the WAR file and not the Ready2Run or source code versions. (In case you're interested, the Ready2Run version of OpenBD is a pre-configured version of Jetty, another servlet container, with OpenBD already deployed.)
With the file downloaded, we now need to deploy it. Tomcat does auto-deployment of any WAR files that are dropped in its webapps directory, so copy the downloaded openbd.war file into {TOMCAT_ROOT}/webapps. Note that for the rest of this how-to guide I'm assuming you did NOT rename the WAR file. This is important once we get into URLs, context paths, and the other fun that comes later. If you know what you're doing and want to call it something else that's fine, just make the necessary adjustments as you go through this.After copying the WAR file to Tomcat's webapps directory, give Tomcat a few seconds to do its deployment magic. If you're watching the directory you'll notice that an openbd directory will be created automatically; this is Tomcat expanding the WAR file. That's it, you just deployed a WAR file.You may now browse to http://localhost:8080/openbd and you'll see OpenBD's test page:
Congratulations! You now have an instance of OpenBD running on Tomcat.
But what about Apache as the web server? Why is there a port number and that silly "/openbd" bit in the URL? Glad you asked, because the rest of this guide deals with making our base install more "production ready."Virtual Hosts in ApacheStrictly speaking you don't need to use Apache as your web server. Tomcat has a very nice, very fast, production-quality HTTP connector called Coyote built in, and I'll probably cover using Tomcat without Apache in another blog post at some point. It does simplify the overall setup and if you don't need what Apache has to offer, it's one less moving part in your setup.That being said, there are plenty of reasons to use Apache. Whether it's familiarity, or you need to run Perl, PHP, etc. on the same physical box as OpenBD, or you need Apache features that don't exist in Tomcat's web server, it's not at all uncommon to use Apache as the front-end web server in a setup like this.The first thing we'll cover as far as Apache configuration goes is virtual hosting. Simply put, virtual hosting is the way you tell Apache to respond to different host names (e.g. foo.com vs. bar.com) on the same IP address. In IIS speak these are called "web sites," and it's the exact same concept. This is very handy for both production servers and development environments, so as a web developer you owe it to yourself to become well-versed in configuring virtual hosts.By default virtual hosts are disabled, so the first step is to enable them. Open up {APACHE_HOME}/conf/httpd.conf, which is the main Apache configuration file, in a text editor. Scroll down towards the bottom of the file and look for the line"# Virtual hosts" (without the quotes), or you could also do a search for that text. The line immediately following is "#Include conf/extra/httpd-vhosts.conf" (again without the quotes).As you might guess, this directive includes the file conf/extra/httpd-vhosts.conf. The # is a comment in the Apache configuration file, so just delete the # from the Include line (NOT from the # Virtual hosts line since that actually is just a comment line), and save the file.Any time you edit any of the Apache configuration files you need to restart Apache, but hold off on that for a minute because we have another file to edit. Now that we have virtual hosts enabled, which is what uncommenting the line in the last step accomplished, we need to configure a virtual host for localhost, because otherwise Apache won't know what to do when we ask for localhost in our browser.Open {APACHE_HOME}/conf/extra/httpd-vhosts.conf in a text editor. At the bottom of that file you'll see some existing <VirtualHost> blocks. You can leave those as is for reference or delete them; at a minimum I'd say comment them out by adding a # to the beginning of each line, but even if you don't they won't be hurting anything.After the existing <VirtualHost> blocks we're going to add a new one of our own. So add a couple of line breaks to the end of the file, and type the following:<VirtualHost *:80>
After the restart, hit http://locahost in your browser to make sure everything is still working.
That's all well and good but at this point we still don't have Apache and Tomcat on speaking terms. There are many ways to accomplish this, so let's look at one now.Basic HTTP Proxying With mod_proxyThe idea behind proxying is, as the term itself implies, configuring Apache to serve as a go-between that handles requests coming into your web server and hands them off to Tomcat. Note that this is a bit different than the perhaps more familiar scenario of letting the web server handle the static content and only leveraging the CFML engine to do the CFML processing based on file extension. You can do things this way as well, but personally I prefer the proxy route because I find it simpler and more flexible. If you're famliar with mod_proxy and HTTP proxying, and you're thinking "but the performance sucks!", A) it doesn't really suck that badly, and B) we'll go a different route in a bit.Done wrong proxying can be very dangerous so I strongly encourage you to read the Apache docs on mod_proxy. Call my old-fashioned but I like reading the manuals for things I use, at least to the point where I'm dangerous and can write up long blog posts teaching other people to be equally dangerous.By default mod_proxy is disabled (unless you're on a Mac), and the default settings when you do enable it are safe, but as with everything if you don't know what the pitfalls are you can get into trouble. Specifically make sure you always have ProxyRequests set to Off, which it is by default, so I'm merely reiterating how important this is. If you set ProxyRequests to On, you may wind up with an open proxy server and the miscreants trolling the tubes LOVE open proxy servers.So let's throw caution to the wind (seriously, it'll be fine) and enable mod_proxy. To accomplish this, navigate to {APACHE_HOME}/conf and open up httpd.conf in a text editor. Scroll down and look (or do a find) for "#LoadModule proxy_module modules/mod_proxy.so" (without the quotes) and uncomment that line.This only enables the base Apache proxy module, which is necessary for all proxying, but there are add-on modules that handle specific kinds of proxying. At this point we want to do HTTP proxying so we'll need to enable the HTTP proxy module. This allows HTTP requests coming into Apache to be handed off (or "proxied out") to Tomcat. Scroll down a bit more (it's about 5 lines further down in my conf file) and find "#LoadModule proxy_http_module modules/mod_proxy_http.so" Uncomment that line, and save the file.Don't restart Apache just yet--there's one more step involved.Proxying to OpenBD From ApacheThis is the moment you've all been waiting for (and if I didn't include all my poor attempts at witticism we would have gotten here sooner): we're now going to proxy to OpenBD from Apache. What this will do is eliminate the need to use the port and context path in the URL.In order to accomplish this we simply need to add a new virtual host to Apache that proxies to OpenBD. Remember that virtual hosts are used to allow Apache to respond to multiple host names, and within each virtual host block we provide settings specific to that virtual host.Navigate to your {APACHE_HOME}/conf/extra directory and open httpd-vhosts.conf in a text editor. We created a virtual host for localhost earlier so this should be semi-familiar. Scroll to the bottom of the file and add the following virtual host:<VirtualHost *:80>In addition to performance advantages, AJP also retains all of the original CGI variables by default. There are still some CGI problems, however, specifically with CGI.PATH_INFO, and this can wreak havoc with SES URLs that rely on this CGI variable. We'll address that a bit later. Just know that if you need important CGI variables like those listed above, they are retained by AJP. You can get the full skinny on AJP in the AJP docs.
Luckily since we already have the basic pieces for proxying in place, making the switch to AJP is pretty simple.First, navigate to {APACHE_HOME}/conf and open httpd.conf in a text editor. Find the line "#LoadModule proxy_ajp_module modules/mod_proxy_ajp.so" and uncomment it. While you're in there you can disable HTTP proxying by commenting out the HTTP proxy line, but note that you DO need the base mod_proxy module enabled to do any proxying, which includes AJP. Having HTTP proxying doesn't hurt anything other than perhaps causing Apache to take up a bit more RAM, so either leave it enabled or disable it if you choose.Once you have the AJP module enabled, save the httpd.conf file, but before restarting Apache we have to modify our virtual host settings as well.Since AJP is its own protocol, you're probably already guessing that the http://localhost:8080/openbd/ bit in our proxy settings from above will no longer work (or won't be using AJP at any rate) since it's proxying over HTTP. To use AJP we have to make some adjustments in our virtual host settings, so open up {APACHE_HOME}/conf/extra/httpd-vhosts.conf in a text editor. Find the <VirtualHost> block we added above for HTTP proxying, and change it to the following:<VirtualHost *:80>
The context path is now gone thanks to our host on Tomcat, but we're still hitting Tomcat using the port in order to bypass Apache, which lets us make sure our Tomcat virtual host is working properly. This method of hitting Tomcat remains available even once we hook Apache and Tomcat together, and it's a handy way to test Tomcat without going through Apache. If you ever run into any issues, this can help you troubleshoot which piece of your setup is having problems.
Finalizing the Apache Virtual Host ConfigurationBy now you're an old hat with this. There's one more tweak we need to make to our virtual host on the Apache side to get it talking to our newly created host on Tomcat. Open up Apache's httpd-vhosts.conf ({APACHE_HOME}/conf/extra/httpd-vhosts.conf) in a text editor and find the openbd.local virtual host we've been editing. All we need to do now is point to the Tomcat host instead of localhost and the context path:<VirtualHost *:80>
The settings at the bottom are the ones we're concerned with.
In most cases you should only have to worry about the first two. All the reading I did on thread stack size basically said "don't worry about it unless you have a problem," so that's good enough for me.
So how much memory should you give Tomcat? Well buddy, how much RAM you got? Seriously though, particularly if you're going to be running Tomcat as the main application on a server, I'd give it as much RAM as you can. On a 32-bit machine with 2GB of RAM (just for a point of reference), I'd set the initial memory pool and the maximum memory pool both at 1024MB. If you have 4GB available, you can probably go 1.5 times that (realize as you approach 2GB allocated to 32-bit Java you're hitting its limits), but Tomcat seems to run pretty darn well in 1GB depending on how much you're hammering on it. If you're on 64-bit adjust accordingly.Why set both the min and max memory settings to the same value? The theory is that you'll get better performance because Java's going to take up all the RAM it will ever use right when Tomcat fires up as opposed to having to grab more memory as the load increases. Also, if Java tries to grab memory at runtime and that memory isn't available, bad things may happen. Some people also believe that garbage collection runs more efficiently when the min and max memory settings are the same. I've had good luck with setting these to be the same so that's what I tend to do.So set those values, hit "Apply" or "OK," and then restart Tomcat. You're now production ready.If you're interested in learning more about Tomcat memory settings and issues, you can read more in the Tomcat Memory FAQs. I particularly enjoy the answers to the "Why do I get OutOfMemory Errors?" question. So helpful, and yet so snarky!What About mod_jk?If you're totally sadistic, you may want to dig into mod_jk. Then again, if you really need mod_jk, chances are you care enough to not be using Windows. If you're interested in mod_jk on Linux, check out Dave Shuck's blog. Either he's way crazier than I am or I'm way lazier than he is. This how-to guide's long enough as it is, so maybe I'll cover mod_jk another time. I personally like AJP proxying (and mod_jk actually uses AJP as its protocol) because it's fast, flexible, and easy to set up, but there are cases where mod_jk will be a better (or necessary) choice, e.g. more advanced load balancing, if you need support for larger packet sizes, etc. For now we'll leave it alone. You can thank me later.Common Errors@Ryan--using this setup the web app won't work unless it has everything it needs in the docBase you set. This isn't the same thing as what you may be used to with a traditional ColdFusion setup. WEB-INF is where all the JAR files you need are, the configuration files, etc. so it won't work unless that's all present in your docBase.
Short answer is to start thinking of your web applications as more self-contained entities, which is the more traditional way of doing things in the Java world.
Matt, my alternate document root with a copied WEB-INF directory does not appear to be a self-contained entity.
I notice that when I browse to /openbd/bluedragon/administrator on the site, I'm actually browsing the administrator files that are under the installed Tomcat root. (I can make changes to those files and see the modified text). So there is an outside dependency there.
That doesn't concern me much, I think its probably a good idea to have all the sites using the same administrator code. But I also notice that when I change administrator settings on my site, the bluedragon.xml file that gets updated is under {TOMCAT_ROOT}, not under my alternate document root. So its definitely not self contained. Where did I go wrong? Do I need to change something in the copied WEB-INF? Maybe something in web.xml? I looked in there but there are no absolute paths.
Could it be my HOST declaration in Tomcat's server.xml file? I notice all my HOST entries have appBase="webapps". I'm not sure what this setting is referring to.
Thanks for your help.
I guess my question is why are you using an alternate document root instead of just having everything live in the Tomcat webapps area? And if you're browsing the wrong administrator, there's something else going on with your setup that I'm missing.
What do you mean by "Tomcat root" specifically?
Wow--if you're hitting the administrator and the wrong bluedragon.xml file is getting updated, I'm really missing something with how you've done your setup, or there's something really wrong with my how-to guide. ;-)
If you have EVERYTHING under a self-contained webapp in Tomcat, you won't have these problems.
Why did you put an appBase of webapps in all your hosts? Unless I'm missing what you're trying to do you don't need it. You can read more on that here:
http://tomcat.apache.org/tomcat-6.0-doc/config/host.html
The only host on my setup that has an appBase specified is the localhost host--other than that I just leave it off.
If there's something confusing about the how-to please let me know, and if you need further assistance I'm happy to help.
My Document Root is not under the tomcat installation directory because thats just not how I organize my stuff. My websites are located in D:\websites (many people also use C:\inetpub\wwwroot) on my desktop, and in /home/WWW-data on my server. Having the web site located in under the Tomcat installation directory doesn't seem very logical to me, but I can't exactly say why. If it were a PHP site would you want all your *.php files located under the PHP installation directory?
By "Tomcat root" I meant something like C:\development\Tomcat6\, the installation directory I choose when I installed Tomcat. The administrator files I was referring to under the Tomcat root were in C:\development\Tomcat6\webapps\openbd\bluedragon\administrator.
But I solved the problem of the wrong bluedragon.xml file by removing the appBase="webapps". That was there because I copied it from some other example, maybe when I was setting up Railo.
And I had read that Tomcat document page many times trying to figure out what appBase really indicates, but I just wasn't understanding it.
I also noticed that after removing appBase="webapps", I had to copy the bluedragon directory into my Document Root (the bluedragon directory that contains all the admin cfm files).
So anyway I'm good to go now, thanks a lot.
Regarding the logic of putting stuff under Tomcat, IMO you're still thinking in the traditional CF way, not in the Java web app way. It is TOTALLY NORMAL (and IMO preferred) in the Java world that you DO have EVERYTHING (including your CFML engine!) as part of your Java web app. Then deployment is as simple as dropping a WAR file on your servlet container. The PHP example isn't exactly apt because it works the same way traditional "use the same CFML engine for all my apps" does, and the way PHP does things isn't the way Java does things.
Note that you can set things up in this more "traditional" (as far as non-Java stuff goes) way with OpenBD--that's how our "Ready2Run" download is configured. One OpenBD instance, multiple applications. But that's not really the norm in the Java webapp world.
Also, you can do things like drop OpenBD in as your root application in Tomcat and use rewriting to hand CFML files off to OpenBD for processing, or use mod_jk which is more like the JRun web server connector. Some people prefer that, but it's more of a headache to set up, and I prefer having everything self-contained.
But I'm glad you're bringing all this up--it's a different way of doing things as compared to what people are used to with CF, and it's far more normal in the Java web app world. The way CF is configured has gotten people used to doing things in kind of a "non-Java" way.
That being said, I suppose what you might prefer is using mod_jk which would have things function more like what you're used to. I'll have to dig into that at some point.
Thanks for reading and thanks for all the feedback! As always, happy to help further if need be.
BTW, if you are installing Tomcat on Windows you may run into the issue I just did, where it couldn't detect my timezone correctly. I wrote a blog post about the issue this morning: http://www.stillnetstudios.com/2009/04/11/time-off-railo-bluedragon-tomcat
Thanks Ryan! Good to know.
Matt,
Thanks very much for the presentation and the blog post. Very helpful. One question. What I would like to do is work with one codebase and run it on 2 (or 3) CFML engines to check compatibility as I develop. Is this doable? The hitch might be that the WEB-INF directories couldn't co-exist if they need to share the same parent directory, but maybe this could be worked around using mappings so they can be in separate locations.
?
Nando, I've thought about this too. I think it could be done on Linux or OSX using symbolic links, if you are willing to put your application in a subdirectory. If you don't know what a symbolic link is, its kind of like a Windows shortcut, but much more powerful.
For example, say your web app is located in /var/www/html. You could setup three tomcat applications like:
/opt/tomcat6/webapps/openbd
/opt/tomcat6/webapps/railo
/opt/tomcat6/webapps/cfusion
Then inside each of those folders you would make a symbolic link to your application files in /var/www/html:
ln -s /var/www/html /opt/tomcat6/webapps/openbd/myapp
ln -s /var/www/html /opt/tomcat6/webapps/railo/myapp
ln -s /var/www/html /opt/tomcat6/webapps/cfusion/myapp
Then browse to the sites like:
http://localhost:8080/openbd/myapp
http://localhost:8080/railo/myapp
http://localhost:8080/cfusion/myapp
You'll be browsing the same CFML files using the different engines.
Maybe Matt knows of a better way. I'd love to see a way to do this on Windows.
My initial thought was the same as Ryan's--you can do it with a symlink so long as you don't mind your app being in a subdirectory.
You might also be able to get creative with aliases/rewriting at the web server/tomcat level and potentially accomplish this but I'd have to think on that one.
Hi Matt,
First of all, thanks a lot for posting such a detailed tutorial. Even though I'm not on Windows (but OSX), I learned a lot from this post and your presentation on CFMeetup.
One thing I'd like to point out about the SES URL issue. I set up a Tomcat/Railo installation and I found that I could have URLs ending in /index.cfm/handler/action (while testing a ColdBox app) if I simply added the following servlet mapping in the web.xml file of my application:
CFMLServlet
/index.cfm/*
And my cgi.PATH_INFO variables were coming through just fine. I did this hitting Tomcat directly, though, without proxying (i.e. http://mysite.local:8080/index.cfm/handler/action).
>Whoops (I wondered of those angle brackets would show up). Here again is what I added to my web.xml file:
<servlet-mapping>
<servlet-name>CFMLServlet</servlet-name>
<url-pattern>/index.cfm/*</url-pattern>
</servlet-mapping>
Thanks Tony--great tip!
Ok, I found a way to share code between the CFML engines using symlinks like we discussed, but on WINDOWS! Evidently there is a tool from sysinternals called Junction that allows you to make NTFS "junctions" which are similar to symlinks on Linux. I said similar, not the same - evidently when you delete a junction it deletes the folder it was linked to! So be sure to use the junction tool to delete it, don't just delete it from Windows Explorer.
http://technet.microsoft.com/en-us/sysinternals/bb896768.aspx
I was unable to get Tomcat to start as a windows service on my KickAssVPS account.
I discovered that I needed to copy msvcr71.dll from {JAVA HOME}\bin to {WINDOWS}\system32.
After that, Tomcat fired right up!
Thanks Christian--a quick Scroogle shows that mscvr71.dll is part of the Windows Visual C libraries, so I'm a bit confused as to A) why that was in your java bin directory in the first place, and B) why it would have anything to do with running Tomcat.
Did you update your environment variables so Windows knows where to look for Java? And did you download a fresh copy of Java from Sun's web site?
Hmm...that's interesting. I agree that doesn't make much sense, either given it's a C library.
I am using the latest Java JRE (jdk1.6.0_14, as of this writing) but no, I didn't update my environment variables.
Instead, I used Tomcat's "configure" window to make sure the path the {JAVA}\jre\bin\client\jvm.dll was set correctly.
I don't think you have to point to a DLL in the Tomcat settings, though I'd have to double-check a Windows install to be sure. You should just have to point to the Java lib directory.
You're probably better off downloading a full JDK instead of using a JRE and pointing to the JDK in your environment variables on the server as opposed to pointing Tomcat to a DLL.
these are some great tips. I will be installing apache in the future. thanks alot.
Matt - Did you do a post over eliminating the port number and context path for Tomcat? If not do you have some resources.
@Mike--that's covered in this post if you use Apache as the web server. If you're wanting to run Tomcat standalone on port 80 that's doable too, but you'd have to read the Tomcat docs for that. I didn't cover that here.
Thanks. I found some good info on Tomcat from Corfield's "Railo For Dummies" series.
Very helpful, thanks Matt. A good primer on Apache/Tomcat/OpenBD in general, not just for Windows.
One thing I still have questions about is how the WEB-INF directory is used. I didn't want my document root under the Tomcat installation directory. So when I added a HOST to server.xml, I set the docBase to my real web root. But openBD wouldn't work until I copied my WEB-INF directory there. Can you explain this a little?