001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.servlets;
031:
032: import com.caucho.util.L10N;
033: import com.caucho.util.Alarm;
034: import com.caucho.vfs.Path;
035: import com.caucho.vfs.ReadStream;
036: import com.caucho.vfs.ReadWritePair;
037: import com.caucho.vfs.Vfs;
038: import com.caucho.vfs.WriteStream;
039: import com.caucho.config.types.*;
040:
041: import javax.servlet.GenericServlet;
042: import javax.servlet.ServletException;
043: import javax.servlet.ServletRequest;
044: import javax.servlet.ServletResponse;
045: import javax.servlet.http.HttpServletRequest;
046: import javax.servlet.http.HttpServletResponse;
047: import java.io.IOException;
048: import java.io.InputStream;
049: import java.io.OutputStream;
050: import java.io.PrintWriter;
051: import java.util.ArrayList;
052: import java.util.Enumeration;
053: import java.util.Iterator;
054: import java.util.logging.Level;
055: import java.util.logging.Logger;
056:
057: /**
058: * TCP connection pool for the HTTP proxy
059: */
060: public class TcpPool {
061: static protected final Logger log = Logger.getLogger(TcpPool.class
062: .getName());
063: static final L10N L = new L10N(TcpPool.class);
064:
065: private ArrayList<String> _hosts = new ArrayList<String>();
066: private Server[] _servers;
067: private int _roundRobin;
068: private long _failRecoverTime = 30 * 1000L;
069:
070: /**
071: * Adds an address
072: */
073: public void addAddress(String address) {
074: _hosts.add(address);
075: }
076:
077: /**
078: * Adds a host
079: */
080: public void addHost(String host) {
081: _hosts.add(host);
082: }
083:
084: /**
085: * Sets the fail recover time.
086: */
087: public void setFailRecoverTime(Period period) {
088: _failRecoverTime = period.getPeriod();
089: }
090:
091: public int getServerCount() {
092: return _hosts.size();
093: }
094:
095: /**
096: * Initialize the servlet with the server's sruns.
097: */
098: public void init() throws ServletException {
099: if (_hosts.size() == 0)
100: throw new ServletException(L
101: .l("HttpProxyServlet needs at least one host."));
102:
103: _servers = new Server[_hosts.size()];
104:
105: for (int i = 0; i < _hosts.size(); i++) {
106: String host = _hosts.get(i);
107:
108: _servers[i] = new Server(host);
109: }
110: }
111:
112: /**
113: * Handle the request.
114: */
115: public Server nextServer() {
116: synchronized (this ) {
117: long now = Alarm.getCurrentTime();
118:
119: int startIndex = _roundRobin;
120: _roundRobin = (_roundRobin + 1) % _servers.length;
121:
122: int bestCost = Integer.MAX_VALUE;
123: Server bestServer = null;
124:
125: for (int i = 0; i < _servers.length; i++) {
126: int index = (startIndex + i) % _servers.length;
127:
128: Server server = _servers[index];
129:
130: if (_failRecoverTime < now - server.getLastFailTime()
131: && server.getConnectionCount() < bestCost) {
132: bestServer = server;
133: bestCost = server.getConnectionCount();
134: }
135: }
136:
137: if (bestServer != null)
138: return bestServer;
139:
140: int index = _roundRobin;
141: _roundRobin = (_roundRobin + 1) % _servers.length;
142:
143: return _servers[index];
144: }
145: }
146:
147: class Server {
148: private Path _path;
149: private long _lastFailTime;
150:
151: private int _connectionCount;
152:
153: Server(String host) {
154: if (host.startsWith("http"))
155: _path = Vfs.lookup(host);
156: else
157: _path = Vfs.lookup("http://" + host);
158: }
159:
160: String getURL() {
161: return _path.getURL();
162: }
163:
164: ReadWritePair open(String uri) throws IOException {
165: try {
166: Path path = _path.lookup(uri);
167:
168: ReadWritePair pair = path.openReadWrite();
169:
170: if (pair != null) {
171: synchronized (this ) {
172: _connectionCount++;
173: }
174: }
175:
176: return pair;
177: } catch (IOException e) {
178: fail();
179:
180: throw e;
181: }
182: }
183:
184: long getLastFailTime() {
185: return _lastFailTime;
186: }
187:
188: int getConnectionCount() {
189: return _connectionCount;
190: }
191:
192: void fail() {
193: _lastFailTime = Alarm.getCurrentTime();
194: }
195:
196: void close() {
197: synchronized (this) {
198: _connectionCount--;
199: }
200: }
201: }
202: }
|