001: /*
002: * Copyright 2005-2007 Noelios Consulting.
003: *
004: * The contents of this file are subject to the terms of the Common Development
005: * and Distribution License (the "License"). You may not use this file except in
006: * compliance with the License.
007: *
008: * You can obtain a copy of the license at
009: * http://www.opensource.org/licenses/cddl1.txt See the License for the specific
010: * language governing permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL HEADER in each file and
013: * include the License file at http://www.opensource.org/licenses/cddl1.txt If
014: * applicable, add the following below this CDDL HEADER, with the fields
015: * enclosed by brackets "[]" replaced with your own identifying information:
016: * Portions Copyright [yyyy] [name of copyright owner]
017: */
018:
019: package com.noelios.restlet.ext.jetty;
020:
021: import java.io.IOException;
022:
023: import javax.servlet.ServletException;
024:
025: import org.mortbay.jetty.AbstractConnector;
026: import org.mortbay.jetty.HttpConnection;
027: import org.mortbay.jetty.Server;
028: import org.mortbay.thread.BoundedThreadPool;
029:
030: /**
031: * Abstract Jetty Web server connector. Here is the list of parameters that are
032: * supported: <table>
033: * <tr>
034: * <th>Parameter name</th>
035: * <th>Value type</th>
036: * <th>Default value</th>
037: * <th>Description</th>
038: * </tr>
039: * <tr>
040: * <td>minThreads</td>
041: * <td>int</td>
042: * <td>1</td>
043: * <td>Minimum threads waiting to service requests.</td>
044: * </tr>
045: * <tr>
046: * <td>maxThread</td>
047: * <td>int</td>
048: * <td>255</td>
049: * <td>Maximum threads that will service requests.</td>
050: * </tr>
051: * <tr>
052: * <td>threadMaxIdleTimeMs</td>
053: * <td>int</td>
054: * <td>60000</td>
055: * <td>Time for an idle thread to wait for a request or read.</td>
056: * </tr>
057: * <tr>
058: * <td>lowThreads</td>
059: * <td>int</td>
060: * <td>25</td>
061: * <td>Threshold of remaining threads at which the server is considered as
062: * running low on resources.</td>
063: * </tr>
064: * <tr>
065: * <td>lowResourceMaxIdleTimeMs</td>
066: * <td>int</td>
067: * <td>2500</td>
068: * <td>Time in ms that connections will persist if listener is low on
069: * resources.</td>
070: * </tr>
071: * <tr>
072: * <td>acceptorThreads</td>
073: * <td>int</td>
074: * <td>1</td>
075: * <td>Number of acceptor threads to set.</td>
076: * </tr>
077: * <tr>
078: * <td>acceptQueueSize</td>
079: * <td>int</td>
080: * <td>0</td>
081: * <td>Size of the accept queue.</td>
082: * </tr>
083: * <tr>
084: * <td>headerBufferSize</td>
085: * <td>int</td>
086: * <td>4*1024</td>
087: * <td>Size of the buffer to be used for request and response headers.</td>
088: * </tr>
089: * <tr>
090: * <td>requestBufferSize</td>
091: * <td>int</td>
092: * <td>8*1024</td>
093: * <td>Size of the content buffer for receiving requests.</td>
094: * </tr>
095: * <tr>
096: * <td>responseBufferSize</td>
097: * <td>int</td>
098: * <td>32*1024</td>
099: * <td>Size of the content buffer for sending responses.</td>
100: * </tr>
101: * <tr>
102: * <td>ioMaxIdleTimeMs</td>
103: * <td>int</td>
104: * <td>30000</td>
105: * <td>Maximum time to wait on an idle IO operation.</td>
106: * </tr>
107: * <tr>
108: * <td>soLingerTime</td>
109: * <td>int</td>
110: * <td>1000</td>
111: * <td>SO linger time (see Jetty 6 documentation).</td>
112: * </tr>
113: * <tr>
114: * <td>converter</td>
115: * <td>String</td>
116: * <td>com.noelios.restlet.http.HttpServerConverter</td>
117: * <td>Class name of the converter of low-level HTTP calls into high level
118: * requests and responses.</td>
119: * </tr>
120: * <tr>
121: * <td>useForwardedForHeader</td>
122: * <td>boolean</td>
123: * <td>false</td>
124: * <td>Lookup the "X-Forwarded-For" header supported by popular proxies and
125: * caches and uses it to populate the Request.getClientAddresses() method
126: * result. This information is only safe for intermediary components within your
127: * local network. Other addresses could easily be changed by setting a fake
128: * header and should not be trusted for serious security checks.</td>
129: * </tr>
130: * </table>
131: *
132: * @see <a href="http://jetty.mortbay.org/">Jetty home page</a>
133: * @author Jerome Louvel (contact@noelios.com)
134: */
135: public abstract class JettyServerHelper extends
136: com.noelios.restlet.http.HttpServerHelper {
137: /** The wrapped Jetty server. */
138: private Server wrappedServer;
139:
140: /** The internal Jetty connector. */
141: private AbstractConnector connector;
142:
143: /**
144: * Constructor.
145: *
146: * @param server
147: * The server to help.
148: */
149: public JettyServerHelper(org.restlet.Server server) {
150: super (server);
151: this .connector = null;
152: this .wrappedServer = new WrappedServer(this );
153:
154: // Configuring the thread pool
155: BoundedThreadPool btp = new BoundedThreadPool();
156: btp.setLowThreads(getLowThreads());
157: btp.setMaxIdleTimeMs(getThreadMaxIdleTimeMs());
158: btp.setMaxThreads(getMaxThreads());
159: btp.setMinThreads(getMinThreads());
160: getWrappedServer().setThreadPool(btp);
161: }
162:
163: @Override
164: public void start() throws Exception {
165: if (this .connector == null) {
166: this .connector = createConnector();
167: configure(this .connector);
168: getWrappedServer().addConnector(connector);
169: }
170:
171: getWrappedServer().start();
172: }
173:
174: @Override
175: public void stop() throws Exception {
176: getWrappedServer().stop();
177: }
178:
179: /**
180: * Creates a new internal Jetty connector.
181: *
182: * @return A new internal Jetty connector.
183: */
184: protected abstract AbstractConnector createConnector();
185:
186: /**
187: * Configures the internal Jetty connector.
188: *
189: * @param connector
190: * The internal Jetty connector.
191: */
192: protected void configure(AbstractConnector connector) {
193: if (getServer().getAddress() != null)
194: connector.setHost(getServer().getAddress());
195: connector.setPort(getServer().getPort());
196: connector
197: .setLowResourceMaxIdleTime(getLowResourceMaxIdleTimeMs());
198: connector.setAcceptors(getAcceptorThreads());
199: connector.setAcceptQueueSize(getAcceptQueueSize());
200: connector.setHeaderBufferSize(getHeaderBufferSize());
201: connector.setRequestBufferSize(getRequestBufferSize());
202: connector.setResponseBufferSize(getResponseBufferSize());
203: connector.setMaxIdleTime(getIoMaxIdleTimeMs());
204: connector.setSoLingerTime(getSoLingerTime());
205: }
206:
207: /**
208: * Jetty server wrapped by a parent Restlet HTTP server connector.
209: *
210: * @author Jerome Louvel (contact@noelios.com)
211: */
212: private static class WrappedServer extends org.mortbay.jetty.Server {
213: JettyServerHelper helper;
214:
215: /**
216: * Constructor.
217: *
218: * @param server
219: * The Jetty HTTP server.
220: */
221: public WrappedServer(JettyServerHelper server) {
222: this .helper = server;
223: }
224:
225: /**
226: * Handler method converting a Jetty Connection into a Restlet Call.
227: *
228: * @param connection
229: * The connection to handle.
230: */
231: public void handle(HttpConnection connection)
232: throws IOException, ServletException {
233: helper
234: .handle(new JettyCall(helper.getServer(),
235: connection));
236: }
237: };
238:
239: /**
240: * Returns the minimum threads waiting to service requests.
241: *
242: * @return The minimum threads waiting to service requests.
243: */
244: public int getMinThreads() {
245: return Integer.parseInt(getParameters().getFirstValue(
246: "minThreads", "1"));
247: }
248:
249: /**
250: * Returns the maximum threads that will service requests.
251: *
252: * @return The maximum threads that will service requests.
253: */
254: public int getMaxThreads() {
255: return Integer.parseInt(getParameters().getFirstValue(
256: "maxThreads", "255"));
257: }
258:
259: /**
260: * Returns the time for an idle thread to wait for a request or read.
261: *
262: * @return The time for an idle thread to wait for a request or read.
263: */
264: public int getThreadMaxIdleTimeMs() {
265: return Integer.parseInt(getParameters().getFirstValue(
266: "threadMaxIdleTimeMs", "60000"));
267: }
268:
269: /**
270: * Returns the threshold of remaining threads at which the server is
271: * considered as running low on resources.
272: *
273: * @return The threshold of remaining threads at which the server is
274: * considered as running low on resources.
275: */
276: public int getLowThreads() {
277: return Integer.parseInt(getParameters().getFirstValue(
278: "lowThreads", "25"));
279: }
280:
281: /**
282: * Returns the time in ms that connections will persist if listener is low
283: * on resources.
284: *
285: * @return The time in ms that connections will persist if listener is low
286: * on resources.
287: */
288: public int getLowResourceMaxIdleTimeMs() {
289: return Integer.parseInt(getParameters().getFirstValue(
290: "lowResourceMaxIdleTimeMs", "2500"));
291: }
292:
293: /**
294: * Returns the number of acceptor threads to set.
295: *
296: * @return The number of acceptor threads to set.
297: */
298: public int getAcceptorThreads() {
299: return Integer.parseInt(getParameters().getFirstValue(
300: "acceptorThreads", "1"));
301: }
302:
303: /**
304: * Returns the size of the accept queue.
305: *
306: * @return The size of the accept queue.
307: */
308: public int getAcceptQueueSize() {
309: return Integer.parseInt(getParameters().getFirstValue(
310: "acceptQueueSize", "0"));
311: }
312:
313: /**
314: * Returns the size of the buffer to be used for request and response
315: * headers.
316: *
317: * @return The size of the buffer to be used for request and response
318: * headers.
319: */
320: public int getHeaderBufferSize() {
321: return Integer.parseInt(getParameters().getFirstValue(
322: "headerBufferSize", Integer.toString(4 * 1024)));
323: }
324:
325: /**
326: * Returns the size of the content buffer for receiving requests.
327: *
328: * @return The size of the content buffer for receiving requests.
329: */
330: public int getRequestBufferSize() {
331: return Integer.parseInt(getParameters().getFirstValue(
332: "requestBufferSize", Integer.toString(8 * 1024)));
333: }
334:
335: /**
336: * Returns the size of the content buffer for sending responses.
337: *
338: * @return The size of the content buffer for sending responses.
339: */
340: public int getResponseBufferSize() {
341: return Integer.parseInt(getParameters().getFirstValue(
342: "responseBufferSize", Integer.toString(32 * 1024)));
343: }
344:
345: /**
346: * Returns the maximum time to wait on an idle IO operation.
347: *
348: * @return The maximum time to wait on an idle IO operation.
349: */
350: public int getIoMaxIdleTimeMs() {
351: return Integer.parseInt(getParameters().getFirstValue(
352: "ioMaxIdleTimeMs", "30000"));
353: }
354:
355: /**
356: * Returns the SO linger time (see Jetty 6 documentation).
357: *
358: * @return The SO linger time (see Jetty 6 documentation).
359: */
360: public int getSoLingerTime() {
361: return Integer.parseInt(getParameters().getFirstValue(
362: "soLingerTime", "1000"));
363: }
364:
365: /**
366: * Sets the wrapped Jetty server.
367: *
368: * @param wrappedServer
369: * The wrapped Jetty server.
370: */
371: protected void setWrappedServer(Server wrappedServer) {
372: this .wrappedServer = wrappedServer;
373: }
374:
375: /**
376: * Returns the wrapped Jetty server.
377: *
378: * @return The wrapped Jetty server.
379: */
380: protected Server getWrappedServer() {
381: return this.wrappedServer;
382: }
383:
384: }
|