Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/ajp/tomcat4/Ajp13Processor.java


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;
21  import java.net.Socket;
22  
23  import javax.servlet.ServletException;
24  import javax.servlet.http.HttpServletResponse;
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: 299218 $ $Date: 2004-02-24 03:48:44 -0500 (Tue, 24 Feb 2004) $
37   */
38  
39  final class Ajp13Processor
40      implements Lifecycle, Runnable {
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 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 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 thread = null;
181 
182 
183     /**
184      * The name to register for the background thread.
185      */
186     private String threadName = null;
187 
188 
189     /**
190      * This processor's thread group.
191      */
192     private ThreadGroup threadGroup = null;
193 
194 
195     /**
196      * The thread synchronization object.
197      */
198     private Object threadSync = new Object();
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 socket) {
215 
216         // Wait for the Processor to get the previous Socket
217         while (available) {
218       try {
219           wait();
220       } catch (InterruptedException 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 await() {
243 
244         // Wait for the Connector to provide a new Socket
245         while (!available) {
246       try {
247           wait();
248       } catch (InterruptedException e) {
249       }
250         }
251 
252   // Notify the Connector that we have received this Socket
253   Socket 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 socket)
273         throws IOException, ServletException {
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 socket) {
291 
292         Ajp13 ajp13 = new Ajp13();
293         ajp13.setDebug(debug);
294         ajp13.setLogger(new org.apache.ajp.Logger() {
295                 public void log(String msg) {
296                     logger.log("[Ajp13] " + msg);
297                 }
298                 
299                 public void log(String msg, Throwable 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 e) {
311             logger.log("process: ajp13.setSocket", e);
312         }
313 
314         boolean moreRequests = true;
315         String 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 e) {
333                 logger.log("process: ajp13.receiveNextRequest", e);
334             }
335 
336             if( needAuth ) {
337                 String 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 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 ioe) {
411                         // Pass the IOException through
412                         throw ioe;
413                     } catch (Throwable 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 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 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 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 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 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(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 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 }