KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > web > loadbalancer > Loadbalancer


1 /*
2  * JBoss, the OpenSource WebOS
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7 package org.jboss.web.loadbalancer;
8
9 import java.io.IOException JavaDoc;
10 import java.io.InputStream JavaDoc;
11 import java.io.OutputStream JavaDoc;
12 import java.util.Enumeration JavaDoc;
13 import java.util.HashSet JavaDoc;
14 import java.util.Set JavaDoc;
15 import javax.management.ObjectName JavaDoc;
16 import javax.servlet.ServletException JavaDoc;
17 import javax.servlet.http.Cookie JavaDoc;
18
19 import org.apache.commons.httpclient.Header;
20 import org.apache.commons.httpclient.HttpClient;
21 import org.apache.commons.httpclient.HttpMethod;
22 import org.apache.commons.httpclient.HttpState;
23 import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
24 import org.apache.commons.httpclient.cookie.CookiePolicy;
25 import org.apache.commons.httpclient.methods.DeleteMethod;
26 import org.apache.commons.httpclient.methods.GetMethod;
27 import org.apache.commons.httpclient.methods.HeadMethod;
28 import org.apache.commons.httpclient.methods.OptionsMethod;
29 import org.apache.commons.httpclient.methods.PostMethod;
30 import org.apache.commons.httpclient.methods.PutMethod;
31 import org.jboss.logging.Logger;
32 import org.jboss.web.loadbalancer.scheduler.NoHostAvailableException;
33 import org.jboss.web.loadbalancer.scheduler.SchedulerMBean;
34 import org.jboss.web.loadbalancer.util.Constants;
35 import org.jboss.web.loadbalancer.util.Request;
36
37 /**
38  * The Loadbalancer core class.
39  *
40  * @jmx:mbean name="jboss.web.loadbalancer: service=Loadbalancer"
41  *
42  * @author Thomas Peuss <jboss@peuss.de>
43  * @version $Revision: 1.9 $
44  */

45 public class Loadbalancer
46     implements LoadbalancerMBean
47 {
48   protected static Logger log = Logger.getLogger(Loadbalancer.class);
49
50   // The connection manager
51
protected MultiThreadedHttpConnectionManager connectionManager;
52
53   // request header elements that must not be copied to client
54
protected static Set JavaDoc ignorableHeader = new HashSet JavaDoc();
55
56   protected int connectionTimeout = 20000;
57
58   protected SchedulerMBean scheduler;
59   protected ObjectName JavaDoc schedulerName;
60
61   protected static final int MAX_RETRIES = 5;
62
63   static
64   {
65     // this header elements are not copied from
66
// the HttpClient response to the request client.
67
// and vice versa because they are generated by
68
// the servlet-engine
69
ignorableHeader.add("content-length");
70     ignorableHeader.add("server");
71     ignorableHeader.add("transfer-encoding");
72     ignorableHeader.add("cookie");
73     ignorableHeader.add("set-cookie");
74     ignorableHeader.add("host");
75   }
76
77   protected Loadbalancer(SchedulerMBean scheduler, int timeout) throws ServletException JavaDoc
78   {
79     this.scheduler=scheduler;
80     this.connectionTimeout=timeout;
81
82     connectionManager = new MultiThreadedHttpConnectionManager();
83
84     // We disable this because the web-container limits the maximum connection count anyway
85
connectionManager.setMaxConnectionsPerHost(Integer.MAX_VALUE);
86     connectionManager.setMaxTotalConnections(Integer.MAX_VALUE);
87   }
88
89   /**
90    * Create a HttpMethod object for the given request.
91    * @param request
92    * @param response
93    * @param requestMethod
94    * @return
95    * @throws NoHostAvailableException
96    */

97   protected void createMethod(Request schedRequest) throws NoHostAvailableException
98   {
99     String JavaDoc url = null;
100     HttpMethod method = null;
101
102     scheduler.getHost(schedRequest);
103
104     // get target host from scheduler
105
url = schedRequest.getHost().getUrl().toExternalForm();
106
107     String JavaDoc path = url.substring(0, url.length() - 1) + schedRequest.getRequest().getRequestURI();
108
109     switch (schedRequest.getRequestMethod())
110     {
111       case Constants.HTTP_METHOD_GET:
112         method = new GetMethod(path);
113         break;
114       case Constants.HTTP_METHOD_POST:
115         method = new PostMethod(path);
116         break;
117       case Constants.HTTP_METHOD_DELETE:
118         method = new DeleteMethod(path);
119         break;
120       case Constants.HTTP_METHOD_HEAD:
121         method = new HeadMethod(path);
122         break;
123       case Constants.HTTP_METHOD_OPTIONS:
124         method = new OptionsMethod(path);
125         break;
126       case Constants.HTTP_METHOD_PUT:
127         method = new PutMethod(path);
128         break;
129       default:
130         throw new IllegalStateException JavaDoc("Unknown Request Method " +
131                                         schedRequest.getRequest().getMethod());
132     }
133     schedRequest.setMethod(method);
134   }
135
136   /**
137    * Add the request information to the HttpMethod
138    * @param request
139    * @param method
140    * @return
141    */

142   protected void addRequestData(Request schedRequest)
143   {
144     HttpMethod method=schedRequest.getMethod();
145
146     // add GET-data to query string
147
if (schedRequest.getRequest().getQueryString() != null)
148     {
149       method.setQueryString(schedRequest.getRequest().getQueryString());
150     }
151
152     // add POST-data to the request
153
if (method instanceof PostMethod)
154     {
155       PostMethod postMethod = (PostMethod) method;
156
157       Enumeration JavaDoc paramNames = schedRequest.getRequest().getParameterNames();
158       while (paramNames.hasMoreElements())
159       {
160         String JavaDoc paramName = (String JavaDoc) paramNames.nextElement();
161         postMethod.addParameter(paramName, schedRequest.getRequest().getParameter(paramName));
162       }
163     }
164   }
165
166   /**
167    * Prepare the request to the target node.
168    * @param request
169    * @param response
170    * @param method
171    * @return
172    */

173   protected void prepareServerRequest(Request schedRequest)
174   {
175     HttpMethod method=schedRequest.getMethod();
176
177     // Initialize client
178
HttpClient client = new HttpClient(connectionManager);
179     client.setStrictMode(false);
180     client.setTimeout(connectionTimeout);
181     client.setConnectionTimeout(connectionTimeout);
182     client.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY);
183
184     // Initialize Method
185
method.setFollowRedirects(false);
186     method.setDoAuthentication(false);
187
188     // Add request header to request (minus ignored header values)
189
Enumeration JavaDoc reqHeaders = schedRequest.getRequest().getHeaderNames();
190     while (reqHeaders.hasMoreElements())
191     {
192       String JavaDoc headerName = (String JavaDoc) reqHeaders.nextElement();
193       String JavaDoc headerValue = schedRequest.getRequest().getHeader(headerName);
194
195       if (!ignorableHeader.contains(headerName.toLowerCase()))
196       {
197         method.setRequestHeader(headerName, headerValue);
198       }
199     }
200
201     // Copy cookies into the request
202
Cookie JavaDoc[] cookies = schedRequest.getRequest().getCookies();
203     HttpState state = client.getState();
204     for (int i = 0; cookies != null && i < cookies.length; ++i)
205     {
206       Cookie JavaDoc cookie = cookies[i];
207
208       org.apache.commons.httpclient.Cookie reqCookie =
209           new org.apache.commons.httpclient.Cookie();
210
211       reqCookie.setName(cookie.getName());
212       reqCookie.setValue(cookie.getValue());
213
214       // patch cookie path because the HttpClient does not like null paths
215
if (cookie.getPath() != null)
216       {
217         reqCookie.setPath(cookie.getPath());
218       }
219       else
220       {
221         reqCookie.setPath("/");
222       }
223
224       reqCookie.setSecure(cookie.getSecure());
225
226       reqCookie.setDomain(method.getHostConfiguration().getHost());
227       state.addCookie(reqCookie);
228     }
229     schedRequest.setClient(client);
230   }
231
232   /**
233    * Handle the client request.
234    * @param request
235    * @param response
236    * @param method
237    * @throws ServletException
238    * @throws IOException
239    */

240   protected void handleRequest(Request schedRequest) throws ServletException JavaDoc, IOException JavaDoc
241   {
242
243     boolean reschedule = false;
244
245     try
246     {
247       // prepare the request
248
prepareServerRequest(schedRequest);
249
250       int tries = 0;
251       // we try several times before we give up
252
while (tries < MAX_RETRIES)
253       {
254         try
255         {
256           // GO
257
long t1=System.currentTimeMillis();
258           schedRequest.getHost().incCurrentConnections();
259
260           schedRequest.getClient().executeMethod(schedRequest.getMethod());
261
262           schedRequest.getHost().decCurrentConnections();
263
264           long t2=System.currentTimeMillis();
265           schedRequest.getHost().addRequest((int)(t2-t1));
266           break;
267         }
268         catch (IOException JavaDoc ex)
269         {
270           try
271           {
272             schedRequest.getHost().decCurrentConnections();
273             schedRequest.getMethod().recycle();
274           }
275           catch (Exception JavaDoc e)
276           {
277             //ignore
278
}
279
280           tries++;
281           log.info("Connect retry no. " + tries, ex);
282         }
283       }
284       // everything ok?
285
if (tries < MAX_RETRIES)
286       {
287         // generate the response for the
288
parseServerResponse(schedRequest);
289       }
290       else
291       {
292         log.error("Max retries reached - giving up. Host will be marked down");
293
294         // Inform Scheduler of node problems
295
schedRequest.getHost().markNodeDown();
296         reschedule = true;
297       }
298     }
299     finally
300     {
301       try
302       {
303         schedRequest.getMethod().recycle();
304       }
305       catch (Exception JavaDoc e)
306       {
307         //ignore
308
}
309     }
310     // try again?
311
if (reschedule)
312     {
313       String JavaDoc redirectURI = schedRequest.getRequest().getRequestURI();
314
315       if (schedRequest.getRequest().getQueryString() != null)
316       {
317         redirectURI += "?" + schedRequest.getRequest().getQueryString();
318       }
319       // send redirect to force client request
320
schedRequest.getResponse().sendRedirect(redirectURI);
321     }
322   }
323
324   /**
325    * Copy the server answer meta data to the client.
326    * @param request
327    * @param response
328    * @param client
329    * @param method
330    * @throws ServletException
331    * @throws IOException
332    */

333   protected void parseServerResponse(Request schedRequest) throws ServletException JavaDoc, IOException JavaDoc
334   {
335     schedRequest.getResponse().setStatus(schedRequest.getMethod().getStatusCode());
336
337     //Cookies
338
org.apache.commons.httpclient.Cookie[] respCookies =
339         schedRequest.getClient().getState().getCookies();
340
341     for (int i = 0; i < respCookies.length; ++i)
342     {
343       Cookie JavaDoc cookie =
344           new Cookie JavaDoc(respCookies[i].getName(), respCookies[i].getValue());
345
346       if (respCookies[i].getPath() != null)
347       {
348         cookie.setPath(respCookies[i].getPath());
349       }
350       schedRequest.getResponse().addCookie(cookie);
351     }
352
353     //Header
354
Header[] header = schedRequest.getMethod().getResponseHeaders();
355
356     for (int i = 0; i < header.length; ++i)
357     {
358       if (!ignorableHeader.contains(header[i].getName().toLowerCase()))
359       {
360         schedRequest.getResponse().setHeader(header[i].getName(), header[i].getValue());
361       }
362     }
363
364     copyServerResponse(schedRequest);
365   }
366
367   /**
368    * Copy content to the client.
369    * @param response
370    * @param method
371    * @throws IOException
372    */

373   protected void copyServerResponse(Request schedRequest) throws IOException JavaDoc
374   {
375
376     InputStream JavaDoc bodyStream = schedRequest.getMethod().getResponseBodyAsStream();
377
378     // any response?
379
if (bodyStream == null)
380     {
381       log.debug("No request body");
382       return;
383     }
384
385     byte[] buffer = new byte[2048];
386     int numBytes;
387     OutputStream JavaDoc out = schedRequest.getResponse().getOutputStream();
388
389     // copy the response
390
while ( (numBytes = bodyStream.read(buffer)) != -1)
391     {
392       out.write(buffer, 0, numBytes);
393       if (log.isDebugEnabled())
394       {
395         log.debug("Copied " + numBytes + " bytes");
396       }
397     }
398   }
399
400   // MBean Interface
401
/**
402    * Get the currently used connection timeout to slave hosts.
403    * @jmx:managed-attribute
404    */

405   public int getConnectionTimeout()
406   {
407     return this.connectionTimeout;
408   }
409
410   /**
411    * Set the currently used connection timeout to slave hosts.
412    * @jmx:managed-attribute
413    */

414   public void setConnectionTimeout(int newTimeout)
415   {
416     this.connectionTimeout = newTimeout;
417   }
418
419   /**
420    * Get the currently used connections to slave hosts.
421    * @jmx:managed-attribute
422    */

423   public int getConnectionsInUse()
424   {
425     return connectionManager.getConnectionsInUse();
426   }
427 }
428
Popular Tags