// Copyright 2011 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.enterprise.connector.loadbalancer;
import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import java.io.IOException;
import java.util.Arrays;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
/**
* A Tomcat Valve that does round-robin load balancing to the
* configured Connector Manager web applications.
*/
public class LoadBalancerValve extends ValveBase {
protected String manager = "/connector-manager";
protected String[] workers = {"/connector-manager-1", "/connector-manager-2"};
protected volatile int workerIndex = 0;
/**
* Sets the Connector Manager to proxy. Requests to this connector manager
* will be farmed out to the various workers.
*
* @param manager the proxied Connector Manager (default "connector-manager")
*/
public void setManager(String manager) {
if (manager == null || manager.length() == 0) {
throw new IllegalArgumentException("manager must not be null or empty");
}
this.manager = (manager.charAt(0) == '/') ? manager : ("/" + manager);
}
/**
* Sets the list of Connector Managers web applications to distribute work to.
*
* @param workers a comma-separated list of connector manager web apps
* (default "connector-manager-1,connector-manager-2")
*/
public void setWorkers(String workers) {
if (workers == null || workers.length() == 0) {
throw new IllegalArgumentException("workers must not be null or empty");
}
this.workers = workers.split("\\s*,\\s*");
this.workerIndex = 0;
}
/** Return descriptive information about this Valve implementation. */
@Override
public String getInfo() {
return "Google Connector Manager Load Balancing Valve - distribute "
+ manager + " requests to " + Arrays.toString(workers);
}
/**
* The implementation-specific logic represented by this Valve.
*
* @param request The servlet request to be processed
* @param response The servlet response to be created
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet error occurs
*/
@Override
public void invoke(Request request, Response response)
throws IOException, ServletException {
if (request.getContextPath() == null ||
request.getContextPath().length() == 0) {
String servletPath = request.getServletPath();
if (servletPath.equals(manager)) {
servletPath = manager + "/";
}
if (servletPath.startsWith(manager + "/")) {
String newContextPath = (workers[workerIndex].charAt(0) == '/')
? workers[workerIndex]
: ("/" + workers[workerIndex]);
if (++workerIndex == workers.length) {
workerIndex = 0;
}
String newServletPath = servletPath.substring(manager.length());
containerLog.info("BalancerValve redirecting to: " + newContextPath
+ newServletPath);
ServletContext workerContext =
request.getContext().getServletContext().getContext(newContextPath);
RequestDispatcher workerDispatcher =
workerContext.getRequestDispatcher(newServletPath);
workerDispatcher.forward(request, response);
return;
}
}
getNext().invoke(request, response);
}
}