KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ajp > tomcat4 > Ajp13Processor


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.ajp.tomcat4;
18
19
20 import java.io.IOException JavaDoc;
21 import java.net.Socket JavaDoc;
22
23 import javax.servlet.ServletException JavaDoc;
24 import javax.servlet.http.HttpServletResponse JavaDoc;
25
26 import org.apache.ajp.Ajp13;
27 import org.apache.catalina.Lifecycle;
28 import org.apache.catalina.LifecycleException;
29 import org.apache.catalina.LifecycleListener;
30 import org.apache.catalina.util.LifecycleSupport;
31 import org.apache.catalina.util.StringManager;
32 import org.apache.tomcat.util.http.BaseRequest;
33
34 /**
35  * @author Kevin Seguin
36  * @version $Revision: 1.14 $ $Date: 2004/02/24 08:48:41 $
37  */

38
39 final class Ajp13Processor
40     implements Lifecycle, Runnable JavaDoc {
41
42     /**
43      * A simple class to provide synchronized access
44      * to a boolean.
45      */

46     private class Bool {
47
48         private boolean b = false;
49
50         Bool() {
51         }
52         
53         Bool(boolean b) {
54             this.b = b;
55         }
56
57         synchronized boolean value() {
58             return b;
59         }
60
61         synchronized void set(boolean b) {
62             this.b = b;
63         }
64     }
65
66     // ----------------------------------------------------------- Constructors
67

68
69     /**
70      * Construct a new Ajp13Processor associated with the specified connector.
71      *
72      * @param connector Ajp13Connector that owns this processor
73      * @param id Identifier of this Ajp13Processor (unique per connector)
74      * @param threadGroup The thread group any threads created by the processor
75      * should be in.
76      */

77     public Ajp13Processor(Ajp13Connector connector,
78                           int id,
79                           ThreadGroup JavaDoc threadGroup) {
80
81     super();
82     this.connector = connector;
83     this.debug = connector.getDebug();
84     this.id = id;
85     this.request = (Ajp13Request) connector.createRequest();
86         this.request.setConnector(connector);
87         this.request.setConnector(connector);
88     this.response = (Ajp13Response) connector.createResponse();
89         this.response.setConnector(connector);
90     this.threadName =
91       "Ajp13Processor[" + connector.getPort() + "][" + id + "]";
92         this.threadGroup = threadGroup;
93
94         this.logger.setConnector(connector);
95         this.logger.setName(this.threadName);
96     }
97
98
99     // ----------------------------------------------------- Instance Variables
100

101     private Ajp13Logger logger = new Ajp13Logger();
102     private BaseRequest ajpRequest = new BaseRequest();
103
104     /**
105      * Is there a new socket available?
106      */

107     private boolean available = false;
108
109
110     /**
111      * The Ajp13Connector with which this processor is associated.
112      */

113     private Ajp13Connector connector = null;
114
115
116     /**
117      * The debugging detail level for this component.
118      */

119     private int debug = 0;
120
121
122     /**
123      * The identifier of this processor, unique per connector.
124      */

125     private int id = 0;
126
127
128     /**
129      * The lifecycle event support for this component.
130      */

131     private LifecycleSupport lifecycle = new LifecycleSupport(this);
132
133
134     /**
135      * The AJP13 request object we will pass to our associated container.
136      */

137     private Ajp13Request request = null;
138
139
140     /**
141      * The AJP13 response object we will pass to our associated container.
142      */

143     private Ajp13Response response = null;
144
145
146     /**
147      * The string manager for this package.
148      */

149     protected StringManager sm =
150     StringManager.getManager(Constants.PACKAGE);
151
152
153     /**
154      * The socket we are currently processing a request for. This object
155      * is used for inter-thread communication only.
156      */

157     private Socket JavaDoc socket = null;
158
159
160     /**
161      * Has this component been started yet?
162      */

163     private boolean started = false;
164
165
166     /**
167      * The shutdown signal to our background thread
168      */

169     private Bool stopped = new Bool(true);
170
171     /**
172      * Are we currently handling a request?
173      */

174     private Bool handlingRequest = new Bool(false);
175
176
177     /**
178      * The background thread.
179      */

180     private Thread JavaDoc thread = null;
181
182
183     /**
184      * The name to register for the background thread.
185      */

186     private String JavaDoc threadName = null;
187
188
189     /**
190      * This processor's thread group.
191      */

192     private ThreadGroup JavaDoc threadGroup = null;
193
194
195     /**
196      * The thread synchronization object.
197      */

198     private Object JavaDoc threadSync = new Object JavaDoc();
199
200
201
202     // -------------------------------------------------------- Package Methods
203

204
205     /**
206      * Process an incoming TCP/IP connection on the specified socket. Any
207      * exception that occurs during processing must be logged and swallowed.
208      * <b>NOTE</b>: This method is called from our Connector's thread. We
209      * must assign it to our own thread so that multiple simultaneous
210      * requests can be handled.
211      *
212      * @param socket TCP socket to process
213      */

214     synchronized void assign(Socket JavaDoc socket) {
215
216         // Wait for the Processor to get the previous Socket
217
while (available) {
218         try {
219             wait();
220         } catch (InterruptedException JavaDoc e) {
221         }
222         }
223
224     // Store the newly available Socket and notify our thread
225
this.socket = socket;
226     available = true;
227     notifyAll();
228
229     if ((debug > 0) && (socket != null))
230         logger.log(" An incoming request is being assigned");
231
232     }
233
234
235     // -------------------------------------------------------- Private Methods
236

237
238     /**
239      * Await a newly assigned Socket from our Connector, or <code>null</code>
240      * if we are supposed to shut down.
241      */

242     private synchronized Socket JavaDoc await() {
243
244         // Wait for the Connector to provide a new Socket
245
while (!available) {
246         try {
247             wait();
248         } catch (InterruptedException JavaDoc e) {
249         }
250         }
251
252     // Notify the Connector that we have received this Socket
253
Socket JavaDoc socket = this.socket;
254     available = false;
255     notifyAll();
256
257     if ((debug > 0) && (socket != null))
258         logger.log(" The incoming request has been awaited");
259
260     return (socket);
261
262     }
263
264     /**
265      * Parse and record the connection parameters related to this request.
266      *
267      * @param socket The socket on which we are connected
268      *
269      * @exception IOException if an input/output error occurs
270      * @exception ServletException if a parsing error occurs
271      */

272     private void parseConnection(Socket JavaDoc socket)
273         throws IOException JavaDoc, ServletException JavaDoc {
274
275     if (debug > 1)
276         logger.log(" parseConnection: address=" + socket.getInetAddress() +
277         ", port=" + connector.getPort());
278     request.setServerPort(connector.getPort());
279         request.setSocket(socket);
280
281     }
282
283     /**
284      * Process an incoming AJP13 request on the Socket that has been assigned
285      * to this Processor. Any exceptions that occur during processing must be
286      * swallowed and dealt with.
287      *
288      * @param socket The socket on which we are connected to the client
289      */

290     private void process(Socket JavaDoc socket) {
291
292         Ajp13 ajp13 = new Ajp13();
293         ajp13.setDebug(debug);
294         ajp13.setLogger(new org.apache.ajp.Logger() {
295                 public void log(String JavaDoc msg) {
296                     logger.log("[Ajp13] " + msg);
297                 }
298                 
299                 public void log(String JavaDoc msg, Throwable JavaDoc t) {
300                     logger.log("[Ajp13] " + msg, t);
301                 }
302             });
303
304         Ajp13InputStream input = new Ajp13InputStream(ajp13);
305         Ajp13OutputStream output = new Ajp13OutputStream(ajp13);
306         response.setAjp13(ajp13);
307
308         try {
309             ajp13.setSocket(socket);
310         } catch (IOException JavaDoc e) {
311             logger.log("process: ajp13.setSocket", e);
312         }
313
314         boolean moreRequests = true;
315         String JavaDoc expectedSecret=connector.getSecret();
316         
317         boolean needAuth= ( expectedSecret != null );
318         
319         while (moreRequests && !stopped.value()) {
320             
321             int status = 0;
322             try {
323                 if (debug > 0) {
324                     logger.log("waiting on next request...");
325                 }
326                 
327                 status = ajp13.receiveNextRequest(ajpRequest);
328                 
329                 if (debug > 0) {
330                     logger.log("received next request, status=" + status);
331                 }
332             } catch (IOException JavaDoc e) {
333                 logger.log("process: ajp13.receiveNextRequest", e);
334             }
335
336             if( needAuth ) {
337                 String JavaDoc connSecret=ajp13.getSecret();
338                 if( connSecret == null ) {
339                     logger.log( "Connection without password, " +
340                                 "tomcat is configured to require one" );
341                     break;
342                 }
343                 if( ! connSecret.equals(expectedSecret) ) {
344                     logger.log( "Connection with wrong password" );
345                     break;
346                 }
347                 
348                 needAuth=false;
349             }
350             
351             if (stopped.value()) {
352                 if (debug > 0) {
353                     logger.log("process: received request, but we're stopped");
354                 }
355                 break;
356             }
357             
358             if( status==-2) {
359                 // special case - shutdown
360
// XXX need better communication, refactor it
361
// if( !doShutdown(socket.getLocalAddress(),
362
// socket.getInetAddress())) {
363
// moreRequests = false;
364
// continue;
365
// }
366
break;
367             }
368             
369             // Allready handled by low level proto, don't go farther
370
if( status == 999 )
371             {
372                 ajpRequest.recycle();
373                 request.recycle();
374
375                 // recycle ajp13 object
376
ajp13.recycle();
377
378                 continue;
379             }
380
381             if( status != 200 )
382                 break;
383
384             try {
385                 // set flag
386
handlingRequest.set(true);
387
388                 boolean bad_request = false;
389
390                 // set up request
391
try {
392                     request.setAjpRequest(ajpRequest);
393                 } catch (IllegalArgumentException JavaDoc e) {
394                     bad_request = true;
395                 }
396                 request.setResponse(response);
397                 request.setStream(input);
398                 
399                 // setup response
400
response.setRequest(request);
401                 response.setStream(output);
402                 
403                 if (debug > 0) {
404                     logger.log("invoking...");
405                 }
406
407                 if (!bad_request) {
408                     try {
409                         connector.getContainer().invoke(request, response);
410                     } catch (IOException JavaDoc ioe) {
411                         // Pass the IOException through
412
throw ioe;
413                     } catch (Throwable JavaDoc e) {
414                         // A throwable here could be caused by a Valve,
415
// Filter, or other component in the chain.
416
// Processing of the request failed, return an
417
// Internal Server Error
418
logger.log("process: invoke", e);
419                         response.sendError
420                             (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
421                     }
422                 } else {
423                     response.sendError
424                         (HttpServletResponse.SC_BAD_REQUEST);
425                 }
426
427                 if (debug > 0) {
428                     logger.log("done invoking, finishing request/response....");
429                 }
430
431                 response.finishResponse();
432                 request.finishRequest();
433
434                 if (debug > 0) {
435                     logger.log("finished handling request.");
436                 }
437
438             } catch (IOException JavaDoc ioe) {
439                 // Normally this catches a socket Broken Pipe caused by the
440
// remote client aborting the request. Don't print the stack
441
// trace in this case. Then let the Processor recycle.
442
logger.log("process: IOException " + ioe.getMessage());
443                 moreRequests = false;
444             } catch (Throwable JavaDoc e) {
445                 // Processing the request and sending the response failed.
446
// We don't know what the state of the Ajp Connector socket
447
// is in. Bail out and recycle the Processor.
448
logger.log("process: finish", e);
449                 moreRequests = false;
450             }
451
452             // Recycling the request and the response objects
453
if (debug > 0) {
454                 logger.log("recyling objects ...");
455             }
456             
457             ajpRequest.recycle();
458             request.recycle();
459             response.recycle();
460
461             // recycle ajp13 object
462
ajp13.recycle();
463
464             // reset flag
465
handlingRequest.set(false);
466         }
467         
468     try {
469             if (debug > 0) {
470                 logger.log("closing ajp13 object...");
471             }
472
473             ajp13.close();
474
475             if (debug > 0) {
476                 logger.log("ajp13 object closed.");
477             }
478     } catch (IOException JavaDoc e) {
479         logger.log("process: ajp13.close", e);
480     }
481
482     try {
483             if (debug > 0) {
484                 logger.log("closing socket...");
485             }
486
487             socket.close();
488
489             if (debug > 0) {
490                 logger.log("socket closed.");
491             }
492     } catch (IOException JavaDoc e) {
493         logger.log("process: socket.close", e);
494     }
495     socket = null;
496
497         if (debug > 0) {
498             logger.log("process: done");
499         }
500     }
501
502
503     // ---------------------------------------------- Background Thread Methods
504

505
506     /**
507      * The background thread that listens for incoming TCP/IP connections and
508      * hands them off to an appropriate processor.
509      */

510     public void run() {
511
512         // Process requests until we receive a shutdown signal
513
while (!stopped.value()) {
514
515         // Wait for the next socket to be assigned
516
if (debug > 0) {
517                 logger.log("waiting for next socket to be assigned...");
518             }
519         Socket JavaDoc socket = await();
520         if (socket == null)
521         continue;
522
523             if (debug > 0) {
524                 logger.log("socket assigned.");
525             }
526
527         // Process the request from this socket
528
process(socket);
529
530         // Finish up this request
531
if (debug > 0) {
532                 logger.log("recycling myself ...");
533             }
534         connector.recycle(this);
535     }
536
537     // Tell threadStop() we have shut ourselves down successfully
538
synchronized (threadSync) {
539         threadSync.notifyAll();
540     }
541
542     }
543
544
545     /**
546      * Start the background processing thread.
547      */

548     private void threadStart() {
549
550     logger.log(sm.getString("ajp13Processor.starting"));
551
552         stopped.set(false);
553     thread = new Thread JavaDoc(threadGroup, this, threadName);
554     thread.setDaemon(true);
555     thread.start();
556
557     if (debug > 0)
558         logger.log(" Background thread has been started");
559
560     }
561
562
563     /**
564      * Stop the background processing thread.
565      */

566     private void threadStop() {
567
568     logger.log(sm.getString("ajp13Processor.stopping"));
569
570     stopped.set(true);
571         assign(null);
572     synchronized (threadSync) {
573         try {
574                 if (handlingRequest.value()) {
575                     if (debug > 0) {
576                         logger.log
577                             ("currentling handling a request, so waiting....");
578                     }
579                     threadSync.wait(5000);
580                 } else {
581                     if (debug > 0) {
582                         logger.log
583                             ("not currently handling a request, not waiting.");
584                     }
585                 }
586         } catch (InterruptedException JavaDoc e) {
587         ;
588         }
589     }
590     thread = null;
591
592     }
593
594
595     // ------------------------------------------------------ Lifecycle Methods
596

597
598     /**
599      * Add a lifecycle event listener to this component.
600      *
601      * @param listener The listener to add
602      */

603     public void addLifecycleListener(LifecycleListener listener) {
604
605     lifecycle.addLifecycleListener(listener);
606
607     }
608
609     /**
610      * Get the lifecycle listeners associated with this lifecycle. If this
611      * Lifecycle has no listeners registered, a zero-length array is returned.
612      */

613     public LifecycleListener[] findLifecycleListeners() {
614         return null; // FIXME: lifecycle.findLifecycleListeners();
615
}
616
617
618     /**
619      * Remove a lifecycle event listener from this component.
620      *
621      * @param listener The listener to add
622      */

623     public void removeLifecycleListener(LifecycleListener listener) {
624
625     lifecycle.removeLifecycleListener(listener);
626
627     }
628
629
630     /**
631      * Start the background thread we will use for request processing.
632      *
633      * @exception LifecycleException if a fatal startup error occurs
634      */

635     public void start() throws LifecycleException {
636
637     if (started)
638         throw new LifecycleException
639         (sm.getString("ajp13Processor.alreadyStarted"));
640     lifecycle.fireLifecycleEvent(START_EVENT, null);
641     started = true;
642
643     threadStart();
644
645     }
646
647
648     /**
649      * Stop the background thread we will use for request processing.
650      *
651      * @exception LifecycleException if a fatal shutdown error occurs
652      */

653     public void stop() throws LifecycleException {
654
655     if (!started)
656         throw new LifecycleException
657         (sm.getString("ajp13Processor.notStarted"));
658     lifecycle.fireLifecycleEvent(STOP_EVENT, null);
659     started = false;
660
661     threadStop();
662
663     }
664
665
666 }
667
Popular Tags