001: package com.quadcap.net.server;
002:
003: /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.io.IOException;
042:
043: import java.util.Hashtable;
044: import java.util.Iterator;
045: import java.util.Properties;
046:
047: import java.net.ServerSocket;
048:
049: import com.quadcap.util.collections.ArrayQueue;
050: import com.quadcap.util.collections.IntMap;
051:
052: /**
053: * A server has a set of workers, a context, and an acceptor. Workers
054: * are created on demand, up to a configurable maximum.
055: *
056: * @author Stan Bailes
057: */
058: public class Server {
059: Class workerClass;
060: Object workerLock = new Object();
061: Object context;
062: ThreadGroup threads;
063: IntMap allWorkers;
064: ArrayQueue workers;
065: int numWorkers = 0;
066: int maxWorkers = 128;
067: long shutdownInterval = 2000L;
068: String name;
069:
070: /*{com.quadcap.net.server.Server.xml}
071: * <config-var>
072: * <config-name>maxWorkers</config-name>
073: * <config-dflt>64</config-dflt>
074: * <config-desc>QWS uses a thread-pool architecture; new worker threads are
075: * created on demand to satisfy load, up to a a configurable
076: * maximum number, specified by this configuration parameter.</config-desc>
077: * </config-var>
078: *
079: * <config-var>
080: * <config-name>name</config-name>
081: * <config-dflt><i>none</i></config-dflt>
082: * <config-desc>Specify the name of this server.</config-desc>
083: * </config-var>
084: *
085: * <config-var>
086: * <config-name>workerClass</config-name>
087: * <config-dflt><i>none</i></config-dflt>
088: * <config-desc>Class to instantiate to handle requests</config-desc>
089: * </config-var>
090: *
091: * <config-var>
092: * <config-name>shutdownInterval</config-name>
093: * <config-dflt>2000</config-dflt>
094: * <config-desc>Graceful delay on shutdown</config-desc>
095: * </config-var>
096: *
097: * <config-var>
098: * <config-name>port</config-name>
099: * <config-dflt><i>none</i></config-dflt>
100: * <config-desc>TCP Port to accept connections from</config-desc>
101: * </config-var>
102: *
103: * <config-var>
104: * <config-name>queueDepth</config-name>
105: * <config-dflt>16</config-dflt>
106: * <config-desc>TCP listen queue depth</config-desc>
107: * </config-var>
108: */
109: public Server(Properties props, Object context)
110: throws ClassNotFoundException {
111: this .context = context;
112: this .workerClass = Class.forName(props
113: .getProperty("workerClass"));
114:
115: int max = Integer.parseInt(props
116: .getProperty("maxWorkers", "64"));
117: this .workers = new ArrayQueue(max);
118: this .allWorkers = new IntMap(max / 3);
119: this .name = props.getProperty("name", "server");
120: this .threads = new ThreadGroup(name);
121: this .shutdownInterval = Long.parseLong(props.getProperty(
122: "shutdownInterval", "2000"));
123:
124: }
125:
126: public ThreadGroup getThreadGroup() {
127: return threads;
128: }
129:
130: public void startAcceptor(Properties props) throws IOException {
131: int port = Integer.parseInt(props.getProperty("port"));
132: int queueDepth = Integer.parseInt(props.getProperty(
133: "queueDepth", "16"));
134: Acceptor acceptor = new Acceptor(this , port, queueDepth);
135: Thread t = new Thread(threads, acceptor);
136: t.start();
137: }
138:
139: // XXX This implementation doesn't ensure fairness
140: public Worker getIdleWorker() throws Exception {
141: synchronized (workerLock) {
142: while (true) {
143: Worker w = (Worker) workers.popFront();
144: if (w != null)
145: return w;
146:
147: if (numWorkers < maxWorkers) {
148: return newWorker();
149: } else {
150: try {
151: workerLock.wait();
152: } catch (InterruptedException ee) {
153: }
154: }
155: }
156: }
157: }
158:
159: public int getIdleWorkers(Worker[] workerv) throws Exception {
160: int cnt = 0;
161: synchronized (workerLock) {
162: for (; cnt < workerv.length; cnt++) {
163: Worker w = (Worker) workers.popFront();
164: if (w != null) {
165: workerv[cnt] = w;
166: } else {
167: if (numWorkers < maxWorkers) {
168: workerv[cnt] = newWorker();
169: } else if (cnt > 0) {
170: break;
171: } else {
172: try {
173: workerLock.wait();
174: } catch (InterruptedException ee) {
175: }
176: }
177: }
178: }
179: }
180: return cnt;
181: }
182:
183: public void returnIdleWorker(Worker w) {
184: synchronized (workerLock) {
185: workers.pushFront(w);
186: workerLock.notifyAll();
187: }
188: }
189:
190: public void workerDone(Worker w) {
191: allWorkers.remove(w.getId());
192: workerLock.notifyAll();
193: }
194:
195: public Worker newWorker() throws Exception {
196: Worker w = (Worker) workerClass.newInstance();
197: w.init(this , context, name);
198: allWorkers.put(w.getId(), w);
199: new Thread(threads, w).start();
200: return w;
201: }
202:
203: public void stop() {
204: Iterator iter = allWorkers.keys();
205: while (iter.hasNext()) {
206: int id = ((Integer) iter.next()).intValue();
207: Worker w = (Worker) allWorkers.get(id);
208: w.stop();
209: }
210: try {
211: Thread.sleep(shutdownInterval);
212: } catch (Throwable t) {
213: }
214:
215: if (threads.activeCount() > 0) {
216: Thread[] ts = new Thread[threads.activeCount()];
217: int cnt = threads.enumerate(ts);
218: for (int i = 0; i < cnt; i++) {
219: Thread t = ts[i];
220: if (!t.isInterrupted()) {
221: t.interrupt();
222: }
223: }
224: try {
225: Thread.sleep(shutdownInterval);
226: } catch (Throwable t) {
227: }
228: cnt = threads.enumerate(ts);
229: for (int i = 0; i < cnt; i++) {
230: Thread t = ts[i];
231: try {
232: t.join(shutdownInterval / 10);
233: } catch (Throwable th) {
234: } finally {
235: try {
236: t.stop();
237: } catch (Throwable th2) {
238: }
239: }
240: }
241: }
242: }
243: }
|