001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jmeter.protocol.http.proxy;
020:
021: import java.io.IOException;
022: import java.io.InterruptedIOException;
023: import java.net.ServerSocket;
024: import java.net.Socket;
025: import java.util.Collections;
026: import java.util.HashMap;
027: import java.util.Map;
028:
029: import org.apache.jorphan.logging.LoggingManager;
030: import org.apache.log.Logger;
031:
032: /**
033: * Web daemon thread. Creates main socket on port 8080 and listens on it
034: * forever. For each client request, creates a proxy thread to handle the
035: * request.
036: *
037: * @author default Created June 29, 2001
038: * @version $Revision: 571988 $ Last updated: $Date: 2007-09-02 15:19:10 +0100 (Sun, 02 Sep 2007) $
039: */
040: public class Daemon extends Thread {
041: /** Logging */
042: private static transient Logger log = LoggingManager
043: .getLoggerForClass();
044:
045: /** The default port to listen on. */
046: private static final int DEFAULT_DAEMON_PORT = 8080;
047:
048: /** The maximum allowed port to listen on. */
049: private static final int MAX_DAEMON_PORT = 65535;
050:
051: /**
052: * The time (in milliseconds) to wait when accepting a client connection.
053: * The accept will be retried until the Daemon is told to stop. So this
054: * interval is the longest time that the Daemon will have to wait after
055: * being told to stop.
056: */
057: private static final int ACCEPT_TIMEOUT = 1000;
058:
059: /** The port to listen on. */
060: private int daemonPort;
061:
062: /** True if the Daemon is currently running. */
063: private boolean running;
064:
065: /** The target which will receive the generated JMeter test components. */
066: private ProxyControl target;
067:
068: /**
069: * The proxy class which will be used to handle individual requests. This
070: * class must be the {@link Proxy} class or a subclass.
071: */
072: private Class proxyClass = Proxy.class;
073:
074: /** A Map of url string to page character encoding of that page */
075: private Map pageEncodings;
076: /** A Map of url string to character encoding for the form */
077: private Map formEncodings;
078:
079: /**
080: * Default constructor.
081: */
082: public Daemon() {
083: super ("HTTP Proxy Daemon");
084: }
085:
086: /**
087: * Create a new Daemon with the specified port and target.
088: *
089: * @param port
090: * the port to listen on.
091: * @param target
092: * the target which will receive the generated JMeter test
093: * components.
094: */
095: public Daemon(int port, ProxyControl target) {
096: this ();
097: this .target = target;
098: configureProxy(port);
099: }
100:
101: /**
102: * Create a new Daemon with the specified port and target, using the
103: * specified class to handle individual requests.
104: *
105: * @param port
106: * the port to listen on.
107: * @param target
108: * the target which will receive the generated JMeter test
109: * components.
110: * @param proxyClass
111: * the proxy class to use to handle individual requests. This
112: * class must be the {@link Proxy} class or a subclass.
113: */
114: public Daemon(int port, ProxyControl target, Class proxyClass) {
115: this (port, target);
116: this .proxyClass = proxyClass;
117: }
118:
119: /**
120: * Configure the Daemon to listen on the specified port.
121: *
122: * @param _daemonPort
123: * the port to listen on
124: */
125: public void configureProxy(int _daemonPort) {
126: this .daemonPort = _daemonPort;
127: log.info("Proxy: OK");
128: }
129:
130: /**
131: * Main method which will start the Proxy daemon on the specified port (or
132: * the default port if no port is specified).
133: *
134: * @param args
135: * the command-line arguments
136: */
137: public static void main(String args[]) {
138: if (args.length > 1) {
139: System.err.println("Usage: Daemon [daemon port]");
140: log.info("Usage: Daemon [daemon port]");
141: return;
142: }
143:
144: int daemonPort = DEFAULT_DAEMON_PORT;
145: if (args.length > 0) {
146: try {
147: daemonPort = Integer.parseInt(args[0]);
148: } catch (NumberFormatException e) {
149: System.err.println("Invalid daemon port: " + e);
150: log.error("Invalid daemon port", e);
151: return;
152: }
153: if (daemonPort <= 0 || daemonPort > MAX_DAEMON_PORT) {
154: System.err.println("Invalid daemon port");
155: log.error("Invalid daemon port");
156: return;
157: }
158: }
159:
160: Daemon demon = new Daemon();
161: demon.configureProxy(daemonPort);
162: demon.start();
163: }
164:
165: /**
166: * Listen on the daemon port and handle incoming requests. This method will
167: * not exit until {@link #stopServer()} is called or an error occurs.
168: */
169: public void run() {
170: running = true;
171: ServerSocket mainSocket = null;
172:
173: // Maps to contain page and form encodings
174: pageEncodings = Collections.synchronizedMap(new HashMap());
175: formEncodings = Collections.synchronizedMap(new HashMap());
176:
177: try {
178: log.info("Creating Daemon Socket... on port " + daemonPort);
179: mainSocket = new ServerSocket(daemonPort);
180: mainSocket.setSoTimeout(ACCEPT_TIMEOUT);
181: log.info("Proxy up and running!");
182:
183: while (running) {
184: try {
185: // Listen on main socket
186: Socket clientSocket = mainSocket.accept();
187: if (running) {
188: // Pass request to new proxy thread
189: Proxy thd = (Proxy) proxyClass.newInstance();
190: thd.configure(clientSocket, target,
191: pageEncodings, formEncodings);
192: thd.start();
193: } else {
194: // The socket was accepted after we were told to stop.
195: try {
196: clientSocket.close();
197: } catch (IOException e) {
198: // Ignore
199: }
200: }
201: } catch (InterruptedIOException e) {
202: // Timeout occurred. Ignore, and keep looping until we're
203: // told to stop running.
204: }
205: }
206: log.info("Proxy Server stopped");
207: } catch (Exception e) {
208: log.warn("Proxy Server stopped", e);
209: } finally {
210: try {
211: if (mainSocket != null)
212: mainSocket.close();
213: } catch (Exception exc) {
214: }
215: }
216:
217: // Clear maps
218: pageEncodings = null;
219: formEncodings = null;
220: }
221:
222: /**
223: * Stop the proxy daemon. The daemon may not stop immediately.
224: *
225: * see #ACCEPT_TIMEOUT
226: */
227: public void stopServer() {
228: running = false;
229: }
230: }
|