KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mortbay > http > HttpServer


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

16 package org.mortbay.http;
17
18 import java.io.IOException JavaDoc;
19 import java.io.ObjectInputStream JavaDoc;
20 import java.io.ObjectOutputStream JavaDoc;
21 import java.io.Serializable JavaDoc;
22 import java.net.MalformedURLException JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.Collections JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.WeakHashMap JavaDoc;
32
33 import org.apache.commons.logging.Log;
34 import org.mortbay.log.LogFactory;
35 import org.mortbay.http.handler.DumpHandler;
36 import org.mortbay.http.handler.NotFoundHandler;
37 import org.mortbay.http.handler.ResourceHandler;
38 import org.mortbay.util.Container;
39 import org.mortbay.util.EventProvider;
40 import org.mortbay.util.InetAddrPort;
41 import org.mortbay.util.LifeCycle;
42 import org.mortbay.util.LogSupport;
43 import org.mortbay.util.MultiException;
44 import org.mortbay.util.Resource;
45 import org.mortbay.util.StringMap;
46 import org.mortbay.util.ThreadPool;
47 import org.mortbay.util.URI;
48
49
50
51 /* ------------------------------------------------------------ */
52 /** HTTP Server.
53  * Services HTTP requests by maintaining a mapping between
54  * a collection of HttpListeners which generate requests and
55  * HttpContexts which contain collections of HttpHandlers.
56  *
57  * This class is configured by API calls. The
58  * org.mortbay.jetty.Server class uses XML configuration files to
59  * configure instances of this class.
60  *
61  * The HttpServer implements the BeanContext API so that membership
62  * events may be generated for HttpListeners, HttpContexts and WebApplications.
63  *
64  * @see HttpContext
65  * @see HttpHandler
66  * @see HttpConnection
67  * @see HttpListener
68  * @see org.mortbay.jetty.Server
69  * @version $Id: HttpServer.java,v 1.70 2005/12/04 11:43:21 gregwilkins Exp $
70  * @author Greg Wilkins (gregw)
71  */

72 public class HttpServer extends Container
73                         implements LifeCycle,
74                                    EventProvider,
75                                    Serializable JavaDoc
76 {
77     private static Log log = LogFactory.getLog(HttpServer.class);
78     
79     /* ------------------------------------------------------------ */
80     private static WeakHashMap JavaDoc __servers = new WeakHashMap JavaDoc();
81     private static Collection JavaDoc __roServers =
82         Collections.unmodifiableCollection(__servers.keySet());
83     private static String JavaDoc[] __noVirtualHost=new String JavaDoc[1];
84     
85     
86     /* ------------------------------------------------------------ */
87     /** Get HttpServer Collection.
88      * Get a collection of all known HttpServers. Servers can be
89      * removed from this list with the setAnonymous call.
90      * @return Collection of all servers.
91      */

92     public static Collection JavaDoc getHttpServers()
93     {
94         return __roServers;
95     }
96
97     /* ------------------------------------------------------------ */
98     /**
99      * @deprecated User getHttpServers()
100      */

101     public static List JavaDoc getHttpServerList()
102     {
103         return new ArrayList JavaDoc(__roServers);
104     }
105     
106     /* ------------------------------------------------------------ */
107     private List JavaDoc _listeners = new ArrayList JavaDoc(3);
108     private HashMap JavaDoc _realmMap = new HashMap JavaDoc(3);
109     private StringMap _virtualHostMap = new StringMap();
110     private boolean _trace=false;
111     private RequestLog _requestLog;
112     private int _requestsPerGC ;
113     private boolean _resolveRemoteHost =false;
114     
115     private String JavaDoc[] _serverClasses;
116     private String JavaDoc[] _systemClasses;
117     
118     private transient int _gcRequests;
119     private transient HttpContext _notFoundContext=null;
120     private transient boolean _gracefulStop;
121     
122     
123     
124     /* ------------------------------------------------------------ */
125     /** Constructor.
126      */

127     public HttpServer()
128     {
129         this(false);
130     }
131     
132     /* ------------------------------------------------------------ */
133     /** Constructor.
134      * @param anonymous If true, the server is not included in the
135      * static server lists and stopAll methods.
136      */

137     public HttpServer(boolean anonymous)
138     {
139         setAnonymous(anonymous);
140         _virtualHostMap.setIgnoreCase(true);
141     }
142     
143     /* ------------------------------------------------------------ */
144     private void readObject(java.io.ObjectInputStream JavaDoc in)
145         throws IOException JavaDoc, ClassNotFoundException JavaDoc
146     {
147         in.defaultReadObject();
148         HttpListener[] listeners=getListeners();
149         HttpContext[] contexts=getContexts();
150         _listeners.clear();
151         _virtualHostMap.clear();
152         setContexts(contexts);
153         setListeners(listeners);
154         _statsLock=new Object JavaDoc[0];
155     }
156  
157     
158     /* ------------------------------------------------------------ */
159     /**
160      * @param anonymous If true, the server is not included in the
161      * static server lists and stopAll methods.
162      */

163     public void setAnonymous(boolean anonymous)
164     {
165         if (anonymous)
166             __servers.remove(this);
167         else
168             __servers.put(this,__servers);
169     }
170
171     /* ------------------------------------------------------------ */
172     public void setStopGracefully(boolean graceful)
173     {
174     _gracefulStop=graceful;
175     }
176
177     /* ------------------------------------------------------------ */
178     public boolean getStopGracefully()
179     {
180     return _gracefulStop;
181     }
182
183
184     /* ------------------------------------------------------------ */
185     /**
186      * @param listeners Array of HttpListeners.
187      */

188     public void setListeners(HttpListener[] listeners)
189     {
190         List JavaDoc old = new ArrayList JavaDoc(_listeners);
191         
192         for (int i=0;i<listeners.length;i++)
193         {
194             boolean existing=old.remove(listeners[i]);
195             if (!existing)
196                 addListener(listeners[i]);
197         }
198
199         for (int i=0;i<old.size();i++)
200         {
201             HttpListener listener=(HttpListener)old.get(i);
202             removeListener(listener);
203         }
204     }
205     
206     /* ------------------------------------------------------------ */
207     /**
208      * @return Array of HttpListeners.
209      */

210     public HttpListener[] getListeners()
211     {
212         if (_listeners==null)
213             return new HttpListener[0];
214         HttpListener[] listeners=new HttpListener[_listeners.size()];
215         return (HttpListener[])_listeners.toArray(listeners);
216     }
217     
218     
219     /* ------------------------------------------------------------ */
220     /** Create and add a SocketListener.
221      * Conveniance method.
222      * @param address
223      * @return the HttpListener.
224      * @exception IOException
225      */

226     public HttpListener addListener(String JavaDoc address)
227         throws IOException JavaDoc
228     {
229         return addListener(new InetAddrPort(address));
230     }
231     
232     /* ------------------------------------------------------------ */
233     /** Create and add a SocketListener.
234      * Conveniance method.
235      * @param address
236      * @return the HttpListener.
237      * @exception IOException
238      */

239     public HttpListener addListener(InetAddrPort address)
240         throws IOException JavaDoc
241     {
242         HttpListener listener = new SocketListener(address);
243         listener.setHttpServer(this);
244         _listeners.add(listener);
245         addComponent(listener);
246         return listener;
247     }
248     
249     /* ------------------------------------------------------------ */
250     /** Add a HTTP Listener to the server.
251      * @param listener The Listener.
252      * @exception IllegalArgumentException If the listener is not for this
253      * server.
254      */

255     public HttpListener addListener(HttpListener listener)
256         throws IllegalArgumentException JavaDoc
257     {
258         listener.setHttpServer(this);
259         _listeners.add(listener);
260         addComponent(listener);
261         return listener;
262     }
263     
264     /* ------------------------------------------------------------ */
265     /** Remove a HTTP Listener.
266      * @param listener
267      */

268     public void removeListener(HttpListener listener)
269     {
270         if (listener==null)
271             return;
272         
273         for (int l=0;l<_listeners.size();l++)
274         {
275             if (listener.equals(_listeners.get(l)))
276             {
277                 _listeners.remove(l);
278                 removeComponent(listener);
279                 if (listener.isStarted())
280                     try{listener.stop();}catch(InterruptedException JavaDoc e){log.warn(LogSupport.EXCEPTION,e);}
281                 listener.setHttpServer(null);
282             }
283         }
284     }
285
286     
287     /* ------------------------------------------------------------ */
288     public synchronized void setContexts(HttpContext[] contexts)
289     {
290         List JavaDoc old = Arrays.asList(getContexts());
291         
292         for (int i=0;i<contexts.length;i++)
293         {
294             boolean existing=old.remove(contexts[i]);
295             if (!existing)
296                 addContext(contexts[i]);
297         }
298
299         for (int i=0;i<old.size();i++)
300             removeContext((HttpContext)old.get(i));
301     }
302
303     
304     /* ------------------------------------------------------------ */
305     public synchronized HttpContext[] getContexts()
306     {
307         if (_virtualHostMap==null)
308             return new HttpContext[0];
309         
310         ArrayList JavaDoc contexts = new ArrayList JavaDoc(33);
311         Iterator JavaDoc maps=_virtualHostMap.values().iterator();
312         while (maps.hasNext())
313         {
314             PathMap pm=(PathMap)maps.next();
315             Iterator JavaDoc lists=pm.values().iterator();
316             while(lists.hasNext())
317             {
318                 List JavaDoc list=(List JavaDoc)lists.next();
319                 for (int i=0;i<list.size();i++)
320                 {
321                     HttpContext context=(HttpContext)list.get(i);
322                     if (!contexts.contains(context))
323                         contexts.add(context);
324                 }
325             }
326         }
327         return (HttpContext[])contexts.toArray(new HttpContext[contexts.size()]);
328     }
329
330     /* ------------------------------------------------------------ */
331     /** Add a context.
332      * @param context
333      */

334     public HttpContext addContext(HttpContext context)
335     {
336         if (context.getContextPath()==null ||
337             context.getContextPath().length()==0)
338             throw new IllegalArgumentException JavaDoc("No Context Path Set");
339         boolean existing=removeMappings(context);
340         if (!existing)
341         {
342             context.setHttpServer(this);
343             addComponent(context);
344         }
345         addMappings(context);
346         return context;
347     }
348
349     /* ------------------------------------------------------------ */
350     /** Remove a context or Web application.
351      * @exception IllegalStateException if context not stopped
352      */

353     public boolean removeContext(HttpContext context)
354         throws IllegalStateException JavaDoc
355     {
356         if (removeMappings(context))
357         {
358             removeComponent(context);
359             if (context.isStarted())
360                 try{context.stop();} catch (InterruptedException JavaDoc e){log.warn(LogSupport.EXCEPTION,e);}
361             context.setHttpServer(null);
362             return true;
363         }
364         return false;
365     }
366     
367
368     /* ------------------------------------------------------------ */
369     /** Add a context.
370      * As contexts cannot be publicly created, this may be used to
371      * alias an existing context.
372      * @param virtualHost The virtual host or null for all hosts.
373      * @param context
374      */

375     public HttpContext addContext(String JavaDoc virtualHost,
376                                   HttpContext context)
377     {
378         if (virtualHost!=null)
379             context.addVirtualHost(virtualHost);
380         addContext(context);
381         return context;
382     }
383
384
385     /* ------------------------------------------------------------ */
386     /** Create and add a new context.
387      * Note that multiple contexts can be created for the same
388      * virtualHost and contextPath. Requests are offered to multiple
389      * contexts in the order they where added to the HttpServer.
390      * @param contextPath
391      * @return A HttpContext instance created by a call to newHttpContext.
392      */

393     public HttpContext addContext(String JavaDoc contextPath)
394     {
395         HttpContext hc = newHttpContext();
396         hc.setContextPath(contextPath);
397         addContext(hc);
398         return hc;
399     }
400     
401     /* ------------------------------------------------------------ */
402     /** Create and add a new context.
403      * Note that multiple contexts can be created for the same
404      * virtualHost and contextPath. Requests are offered to multiple
405      * contexts in the order they where added to the HttpServer.
406      * @param virtualHost Virtual hostname or null for all hosts.
407      * @param contextPathSpec Path specification relative to the context path.
408      * @return A HttpContext instance created by a call to newHttpContext.
409      */

410     public HttpContext addContext(String JavaDoc virtualHost, String JavaDoc contextPathSpec)
411     {
412         if (virtualHost!=null && virtualHost.length()==0)
413             virtualHost=null;
414         HttpContext hc = newHttpContext();
415         hc.setContextPath(contextPathSpec);
416         if (virtualHost!=null)
417             hc.addVirtualHost(virtualHost);
418         addContext(hc);
419         return hc;
420     }
421     
422     
423     /* ------------------------------------------------------------ */
424     /** Get specific context.
425      * @param virtualHost The virtual host or null for all hosts.
426      * @param contextPathSpec Path specification relative to the context path.
427      * @param i Index among contexts of same virtualHost and pathSpec.
428      * @return The HttpContext or null.
429      */

430     public HttpContext getContext(String JavaDoc virtualHost, String JavaDoc contextPathSpec, int i)
431     {
432         HttpContext hc=null;
433         contextPathSpec=HttpContext.canonicalContextPathSpec(contextPathSpec);
434
435         PathMap contextMap=(PathMap)_virtualHostMap.get(virtualHost);
436         if (contextMap!=null)
437         {
438             List JavaDoc contextList = (List JavaDoc)contextMap.get(contextPathSpec);
439             if (contextList!=null)
440             {
441                 if (i>=contextList.size())
442                     return null;
443                 hc=(HttpContext)contextList.get(i);
444             }
445         }
446
447         return hc;
448     }
449
450     
451     /* ------------------------------------------------------------ */
452     /** Get or create context.
453      * @param virtualHost The virtual host or null for all hosts.
454      * @param contextPathSpec
455      * @return HttpContext. If multiple contexts exist for the same
456      * virtualHost and pathSpec, the most recently added context is returned.
457      * If no context exists, a new context is created by a call to newHttpContext.
458      */

459     public HttpContext getContext(String JavaDoc virtualHost, String JavaDoc contextPathSpec)
460     {
461         HttpContext hc=null;
462         contextPathSpec=HttpContext.canonicalContextPathSpec(contextPathSpec);
463         PathMap contextMap=(PathMap)_virtualHostMap.get(virtualHost);
464         
465         if (contextMap!=null)
466         {
467             List JavaDoc contextList = (List JavaDoc)contextMap.get(contextPathSpec);
468             if (contextList!=null && contextList.size()>0)
469                 hc=(HttpContext)contextList.get(contextList.size()-1);
470         }
471         if (hc==null)
472             hc=addContext(virtualHost,contextPathSpec);
473
474         return hc;
475     }
476     
477     /* ------------------------------------------------------------ */
478     /** Get or create context.
479      * @param contextPathSpec Path specification relative to the context path.
480      * @return The HttpContext If multiple contexts exist for the same
481      * pathSpec, the most recently added context is returned.
482      * If no context exists, a new context is created by a call to newHttpContext.
483      */

484     public HttpContext getContext(String JavaDoc contextPathSpec)
485     {
486         return getContext(null,contextPathSpec);
487     }
488  
489     /* ------------------------------------------------------------ */
490     /** Create a new HttpContext.
491      * Specialized HttpServer classes may override this method to
492      * return subclasses of HttpContext.
493      * @return A new instance of HttpContext or a subclass of HttpContext
494      */

495     protected HttpContext newHttpContext()
496     {
497         return new HttpContext();
498     }
499
500     /* ------------------------------------------------------------ */
501     synchronized void addMapping(String JavaDoc virtualHost, HttpContext context)
502     {
503         // Get the map of contexts
504
PathMap contextMap=(PathMap)_virtualHostMap.get(virtualHost);
505         if (contextMap==null)
506         {
507             contextMap=new PathMap(7);
508             _virtualHostMap.put(virtualHost,contextMap);
509         }
510         
511         // Generalize contextPath
512
String JavaDoc contextPathSpec=
513             HttpContext.canonicalContextPathSpec(context.getContextPath());
514         
515         // Get the list of contexts at this path
516
List JavaDoc contextList = (List JavaDoc)contextMap.get(contextPathSpec);
517         if (contextList==null)
518         {
519             contextList=new ArrayList JavaDoc(1);
520             contextMap.put(contextPathSpec,contextList);
521         }
522         
523         // Add the context to the list
524
contextList.add(context);
525             
526         if(log.isDebugEnabled())log.debug("Added "+context+" for host "+(virtualHost==null?"*":virtualHost));
527     }
528     
529
530     /* ------------------------------------------------------------ */
531     synchronized void addMappings(HttpContext context)
532     {
533         if (context==_notFoundContext)
534             return;
535         
536         String JavaDoc[] hosts=context.getVirtualHosts();
537         if (hosts==null || hosts.length==0)
538             hosts = __noVirtualHost;
539
540         // For each host name
541
for (int h=0;h<hosts.length;h++)
542         {
543             String JavaDoc virtualHost=hosts[h];
544             addMapping(virtualHost,context);
545         }
546     }
547
548
549     /* ------------------------------------------------------------ */
550     synchronized boolean removeMapping(String JavaDoc virtualHost, HttpContext context)
551     {
552         boolean existing=false;
553         if (_virtualHostMap!=null)
554         {
555             PathMap contextMap=(PathMap)_virtualHostMap.get(virtualHost);
556             
557             Iterator JavaDoc i2=contextMap.values().iterator();
558             while(i2.hasNext())
559             {
560                 List JavaDoc contextList = (List JavaDoc)i2.next();
561                 if (contextList.remove(context))
562                     existing=true;
563                 if (contextList.size()==0)
564                     i2.remove();
565             }
566         }
567         return existing;
568     }
569     
570     /* ------------------------------------------------------------ */
571     synchronized boolean removeMappings(HttpContext context)
572     {
573         boolean existing=false;
574         
575         if (_virtualHostMap!=null)
576         {
577             Iterator JavaDoc i1 = _virtualHostMap.keySet().iterator();
578             while(i1.hasNext())
579             {
580                 String JavaDoc virtualHost=(String JavaDoc)i1.next();
581                 if (removeMapping(virtualHost,context))
582                     existing=true;
583             }
584         }
585         return existing;
586     }
587     
588     
589     /* ------------------------------------------------------------ */
590     /**
591      * @return True if the TRACE method is fully implemented.
592      */

593     public boolean getTrace()
594     {
595         return _trace;
596     }
597     
598     /* ------------------------------------------------------------ */
599     /**
600      * @param trace True if the TRACE method is fully implemented.
601      */

602     public void setTrace(boolean trace)
603     {
604         _trace = trace;
605     }
606     
607     /* ------------------------------------------------------------ */
608     /** Get the requests per GC.
609      * If this is set greater than zero, then the System garbage collector
610      * will be invoked after approximately this number of requests. For
611      * predictable response, it is often best to have frequent small runs of
612      * the GC rather than infrequent large runs. The request count is only
613      * approximate as it is not synchronized and multi CPU machines may miss
614      * counting some requests.
615      * @return Approx requests per garbage collection.
616      */

617     public int getRequestsPerGC()
618     {
619         return _requestsPerGC;
620     }
621
622     /* ------------------------------------------------------------ */
623     /** Set the requests per GC.
624      * If this is set greater than zero, then the System garbage collector
625      * will be invoked after approximately this number of requests. For
626      * predictable response, it is often best to have frequent small runs of
627      * the GC rather than infrequent large runs. The request count is only
628      * approximate as it is not synchronized and multi CPU machines may miss
629      * counting some requests.
630      * @param requestsPerGC Approx requests per garbage collection.
631      */

632     public void setRequestsPerGC(int requestsPerGC)
633     {
634         _requestsPerGC = requestsPerGC;
635     }
636
637     /* ------------------------------------------------------------ */
638     /** Set system classes.
639      * @deprecated. Use HttpContext
640      */

641     public void setSystemClasses(String JavaDoc[] classes)
642     {
643         _systemClasses=classes;
644     }
645
646     /* ------------------------------------------------------------ */
647     /** Get system classes.
648      * @deprecated. Use HttpContext
649      */

650     public String JavaDoc[] getSystemClasses()
651     {
652         return _systemClasses;
653     }
654
655     /* ------------------------------------------------------------ */
656     /** Set system classes.
657      * @deprecated. Use HttpContext
658      */

659     public void setServerClasses(String JavaDoc[] classes)
660     {
661         _serverClasses=classes;
662     }
663
664     /* ------------------------------------------------------------ */
665     /** Get system classes.
666      * @deprecated. Use HttpContext
667      */

668     public String JavaDoc[] getServerClasses()
669     {
670         return _serverClasses;
671     }
672
673
674     /* ------------------------------------------------------------ */
675     /** Start all handlers then listeners.
676      * If a subcomponent fails to start, it's exception is added to a
677      * org.mortbay.util.MultiException and the start method continues.
678      * @exception MultiException A collection of exceptions thrown by
679      * start() method of subcomponents of the HttpServer.
680      */

681     protected synchronized void doStart()
682         throws Exception JavaDoc
683     {
684         log.info("Version "+Version.getImplVersion());
685         
686         MultiException mex = new MultiException();
687
688         statsReset();
689         
690         if (log.isDebugEnabled())
691         {
692             log.debug("LISTENERS: "+_listeners);
693             log.debug("HANDLER: "+_virtualHostMap);
694         }
695
696         if (_requestLog!=null && !_requestLog.isStarted())
697         {
698             try{
699                 _requestLog.start();
700             }
701             catch(Exception JavaDoc e){mex.add(e);}
702         }
703         
704         HttpContext[] contexts = getContexts();
705         for (int i=0;i<contexts.length;i++)
706         {
707             HttpContext context=contexts[i];
708             try{context.start();}catch(Exception JavaDoc e){mex.add(e);}
709         }
710         
711         for (int l=0;l<_listeners.size();l++)
712         {
713             HttpListener listener =(HttpListener)_listeners.get(l);
714             listener.setHttpServer(this);
715             if (!listener.isStarted())
716                 try{listener.start();}catch(Exception JavaDoc e){mex.add(e);}
717         }
718
719         mex.ifExceptionThrowMulti();
720     }
721     
722     /* ------------------------------------------------------------ */
723     /** Stop all listeners then all contexts.
724      * Equivalent to stop(false);
725      * @exception InterruptedException If interrupted, stop may not have
726      * been called on everything.
727      */

728     protected synchronized void doStop()
729         throws InterruptedException JavaDoc
730     {
731         for (int l=0;l<_listeners.size();l++)
732         {
733             HttpListener listener =(HttpListener)_listeners.get(l);
734             if (listener.isStarted())
735             {
736                 try{listener.stop();}
737                 catch(Exception JavaDoc e)
738                 {
739                     if (log.isDebugEnabled())
740                         log.warn(LogSupport.EXCEPTION,e);
741                     else
742                         log.warn(e.toString());
743                 }
744             }
745         }
746         
747         HttpContext[] contexts = getContexts();
748         for (int i=0;i<contexts.length;i++)
749         {
750             HttpContext context=contexts[i];
751             context.stop(_gracefulStop);
752         }
753
754         if (_notFoundContext!=null)
755         {
756             _notFoundContext.stop();
757             removeComponent(_notFoundContext);
758         }
759         _notFoundContext=null;
760         
761         if (_requestLog!=null && _requestLog.isStarted())
762             _requestLog.stop();
763     }
764     
765     /* ------------------------------------------------------------ */
766     /** Stop all listeners then all contexts.
767      * @param graceful If true and statistics are on for a context,
768      * then this method will wait for requestsActive to go to zero
769      * before stopping that context.
770      */

771     public synchronized void stop(boolean graceful)
772         throws InterruptedException JavaDoc
773     {
774         boolean ov=_gracefulStop;
775         try
776         {
777             _gracefulStop=graceful;
778             stop();
779         }
780         finally
781         {
782             _gracefulStop=ov;
783         }
784     }
785     
786     /* ------------------------------------------------------------ */
787     /** Join the listeners.
788      * Join all listeners that are instances of ThreadPool.
789      * @exception InterruptedException
790      */

791     public void join()
792         throws InterruptedException JavaDoc
793     {
794         for (int l=0;l<_listeners.size();l++)
795         {
796             HttpListener listener =(HttpListener)_listeners.get(l);
797             if (listener.isStarted() && listener instanceof ThreadPool)
798             {
799                 ((ThreadPool)listener).join();
800             }
801         }
802     }
803     
804     /* ------------------------------------------------------------ */
805     /** Define a virtual host alias.
806      * All requests to the alias are handled the same as request for
807      * the virtualHost.
808      * @deprecated Use HttpContext.addVirtualHost
809      * @param virtualHost Host name or IP
810      * @param alias Alias hostname or IP
811      */

812     public void addHostAlias(String JavaDoc virtualHost, String JavaDoc alias)
813     {
814         log.warn("addHostAlias is deprecated. Use HttpContext.addVirtualHost");
815         Object JavaDoc contextMap=_virtualHostMap.get(virtualHost);
816         if (contextMap==null)
817             throw new IllegalArgumentException JavaDoc("No Such Host: "+virtualHost);
818         _virtualHostMap.put(alias,contextMap);
819     }
820
821     /* ------------------------------------------------------------ */
822     /** Set the request log.
823      * @param log RequestLog to use.
824      */

825     public synchronized void setRequestLog(RequestLog log)
826     {
827         if (_requestLog!=null)
828             removeComponent(_requestLog);
829         _requestLog=log;
830         if (_requestLog!=null)
831             addComponent(_requestLog);
832     }
833
834     
835     /* ------------------------------------------------------------ */
836     public RequestLog getRequestLog()
837     {
838         return _requestLog;
839     }
840     
841
842     /* ------------------------------------------------------------ */
843     /** Log a request to the request log
844      * @param request The request.
845      * @param response The response generated.
846      * @param length The length of the body.
847      */

848     void log(HttpRequest request,
849              HttpResponse response,
850              int length)
851     {
852         if (_requestLog!=null &&
853             request!=null &&
854             response!=null)
855             _requestLog.log(request,response,length);
856     }
857     
858     /* ------------------------------------------------------------ */
859     /** Service a request.
860      * Handle the request by passing it to the HttpHandler contained in
861      * the mapped HttpContexts.
862      * The requests host and path are used to select a list of
863      * HttpContexts. Each HttpHandler in these context is offered
864      * the request in turn, until the request is handled.
865      *
866      * If no handler handles the request, 404 Not Found is returned.
867      *
868      * @param request
869      * @param response
870      * @return The HttpContext that completed handling of the request or null.
871      * @exception IOException
872      * @exception HttpException
873      */

874     public HttpContext service(HttpRequest request,HttpResponse response)
875         throws IOException JavaDoc, HttpException
876     {
877         String JavaDoc host=request.getHost();
878
879         if (_requestsPerGC>0 && _gcRequests++>_requestsPerGC)
880         {
881             _gcRequests=0;
882             System.gc();
883         }
884         
885         while (true)
886         {
887             PathMap contextMap=(PathMap)_virtualHostMap.get(host);
888             if (contextMap!=null)
889             {
890                 List JavaDoc contextLists =contextMap.getMatches(request.getPath());
891                 if(contextLists!=null)
892                 {
893                     if(log.isTraceEnabled())log.trace("Contexts at "+request.getPath()+": "+contextLists);
894                     
895                     for (int i=0;i<contextLists.size();i++)
896                     {
897                         Map.Entry JavaDoc entry=
898                             (Map.Entry JavaDoc)
899                             contextLists.get(i);
900                         List JavaDoc contextList = (List JavaDoc)entry.getValue();
901                 
902                         for (int j=0;j<contextList.size();j++)
903                         {
904                             HttpContext context=
905                                 (HttpContext)contextList.get(j);
906                             
907                             if(log.isDebugEnabled())log.debug("Try "+context+","+j);
908
909                             context.handle(request,response);
910                             if (request.isHandled())
911                                 return context;
912                         }
913                     }
914                 }
915             }
916             
917             // try no host
918
if (host==null)
919                 break;
920             host=null;
921         }
922
923         synchronized(this)
924         {
925             if (_notFoundContext==null)
926             {
927                 _notFoundContext=new HttpContext();
928                 _notFoundContext.setContextPath("/");
929                 _notFoundContext.setHttpServer(this);
930
931                 try
932                 {
933                     _notFoundContext
934                         .addHandler((NotFoundHandler)Class.forName
935                                     ("org.mortbay.http.handler.RootNotFoundHandler").newInstance());
936                 }
937                 catch (Exception JavaDoc e)
938                 {
939                     _notFoundContext.addHandler(new NotFoundHandler());
940                 }
941                 
942                 addComponent(_notFoundContext);
943                 try{_notFoundContext.start();}catch(Exception JavaDoc e){log.warn(LogSupport.EXCEPTION,e);}
944             }
945             
946             _notFoundContext.handle(request,response);
947             if (!request.isHandled())
948                 response.sendError(HttpResponse.__404_Not_Found);
949             return _notFoundContext;
950         }
951     }
952     
953     /* ------------------------------------------------------------ */
954     /** Find handler.
955      * Find a handler for a URI. This method is provided for
956      * the servlet context getContext method to search for another
957      * context by URI. A list of hosts may be passed to qualify the
958      * search.
959      * @param uri URI that must be satisfied by the servlet handler
960      * @param vhosts null or a list of virtual hosts names to search
961      * @return HttpHandler
962      */

963     public HttpHandler findHandler(Class JavaDoc handlerClass,
964                                    String JavaDoc uri,
965                                    String JavaDoc[] vhosts)
966     {
967         uri = URI.stripPath(uri);
968
969         if (vhosts==null || vhosts.length==0)
970             vhosts=__noVirtualHost;
971         
972         for (int h=0; h<vhosts.length ; h++)
973         {
974             String JavaDoc host = vhosts[h];
975             
976             PathMap contextMap=(PathMap)_virtualHostMap.get(host);
977             if (contextMap!=null)
978             {
979                 List JavaDoc contextLists =contextMap.getMatches(uri);
980                 if(contextLists!=null)
981                 {
982                     
983                     for (int i=0;i<contextLists.size();i++)
984                     {
985                         Map.Entry JavaDoc entry=
986                             (Map.Entry JavaDoc)
987                             contextLists.get(i);
988                         
989                         List JavaDoc contextList = (List JavaDoc)entry.getValue();
990                 
991                         for (int j=0;j<contextList.size();j++)
992                         {
993                             HttpContext context=
994                                 (HttpContext)contextList.get(j);
995
996                             HttpHandler handler = context.getHandler(handlerClass);
997
998                             if (handler!=null)
999                                 return handler;
1000                        }
1001                    }
1002                }
1003            }
1004        }
1005        return null;
1006    }
1007    
1008    /* ------------------------------------------------------------ */
1009    public UserRealm addRealm(UserRealm realm)
1010    {
1011        return (UserRealm)_realmMap.put(realm.getName(),realm);
1012    }
1013    
1014    /* ------------------------------------------------------------ */
1015    /** Get a named UserRealm.
1016     * @param realmName The name of the realm or null.
1017     * @return The named realm. If the name is null and only a single realm
1018     * is known, that is returned.
1019     */

1020    public UserRealm getRealm(String JavaDoc realmName)
1021    {
1022        if (realmName==null)
1023        {
1024            if (_realmMap.size()==1)
1025                return (UserRealm)_realmMap.values().iterator().next();
1026            log.warn("Null realmName with multiple known realms");
1027        }
1028        return (UserRealm)_realmMap.get(realmName);
1029    }
1030    
1031    /* ------------------------------------------------------------ */
1032    public UserRealm removeRealm(String JavaDoc realmName)
1033    {
1034        return (UserRealm)_realmMap.remove(realmName);
1035    }
1036    
1037
1038    /* ------------------------------------------------------------ */
1039    public Map JavaDoc getHostMap()
1040    {
1041        return _virtualHostMap;
1042    }
1043
1044    /* ------------------------------------------------------------ */
1045    /**
1046     * @return True if the remote host name of connections is resolved.
1047     */

1048    public boolean getResolveRemoteHost()
1049    {
1050        return _resolveRemoteHost;
1051    }
1052    
1053    /* ------------------------------------------------------------ */
1054    /**
1055     * @param resolveRemoteHost True if the remote host name of connections is resolved.
1056     */

1057    public void setResolveRemoteHost(boolean resolveRemoteHost)
1058    {
1059        _resolveRemoteHost = resolveRemoteHost;
1060    }
1061
1062
1063    /* ------------------------------------------------------------ */
1064    private boolean _statsOn=false;
1065    private transient Object JavaDoc _statsLock=new Object JavaDoc[0];
1066    
1067    private transient long _statsStartedAt=0;
1068    
1069    private transient int _connections; // total number of connections made to server
1070

1071    private transient int _connectionsOpen; // number of connections currently open
1072
private transient int _connectionsOpenMin; // min number of connections open simultaneously
1073
private transient int _connectionsOpenMax; // max number of connections open simultaneously
1074

1075    private transient long _connectionsDurationMin; // min duration of a connection
1076
private transient long _connectionsDurationMax; // max duration of a connection
1077
private transient long _connectionsDurationTotal; // total duration of all coneection
1078

1079    private transient int _errors; // total bad requests to the server
1080
private transient int _requests; // total requests made to the server
1081

1082    private transient int _requestsActive; // number of requests currently being handled
1083
private transient int _requestsActiveMin; // min number of connections handled simultaneously
1084
private transient int _requestsActiveMax; // max number of connections handled simultaneously
1085

1086    private transient int _connectionsRequestsMin; // min requests per connection
1087
private transient int _connectionsRequestsMax; // max requests per connection
1088

1089    private transient long _requestsDurationMin; // min request duration
1090
private transient long _requestsDurationMax; // max request duration
1091
private transient long _requestsDurationTotal; // total request duration
1092

1093    
1094    
1095    /* ------------------------------------------------------------ */
1096    /** Reset statistics.
1097     */

1098    public void statsReset()
1099    {
1100        _statsStartedAt=System.currentTimeMillis();
1101
1102        _connections=0;
1103        
1104        _connectionsOpenMin=_connectionsOpen;
1105        _connectionsOpenMax=_connectionsOpen;
1106        _connectionsOpen=0;
1107        
1108        _connectionsDurationMin=0;
1109        _connectionsDurationMax=0;
1110        _connectionsDurationTotal=0;
1111
1112        _errors=0;
1113        _requests=0;
1114
1115        _requestsActiveMin=_requestsActive;
1116        _requestsActiveMax=_requestsActive;
1117        _requestsActive=0;
1118        
1119        _connectionsRequestsMin=0;
1120        _connectionsRequestsMax=0;
1121
1122        _requestsDurationMin=0;
1123        _requestsDurationMax=0;
1124        _requestsDurationTotal=0;
1125    }
1126    
1127    /* ------------------------------------------------------------ */
1128    public void setStatsOn(boolean on)
1129    {
1130        log.info("Statistics on = "+on+" for "+this);
1131        _statsOn=on;
1132    }
1133    
1134    /* ------------------------------------------------------------ */
1135    /**
1136     * @return True if statistics collection is turned on.
1137     */

1138    public boolean getStatsOn()
1139    {
1140        return _statsOn;
1141    }
1142    
1143    /* ------------------------------------------------------------ */
1144    /**
1145     * @return Timestamp stats were started at.
1146     */

1147    public long getStatsOnMs()
1148    {
1149        return _statsOn?(System.currentTimeMillis()-_statsStartedAt):0;
1150    }
1151    
1152    
1153
1154    /* ------------------------------------------------------------ */
1155    /**
1156     * @return Returns the connectionsDurationMin.
1157     */

1158    public long getConnectionsDurationMin()
1159    {
1160        return _connectionsDurationMin;
1161    }
1162
1163    /* ------------------------------------------------------------ */
1164    /**
1165     * @return Returns the connectionsDurationTotal.
1166     */

1167    public long getConnectionsDurationTotal()
1168    {
1169        return _connectionsDurationTotal;
1170    }
1171
1172    /* ------------------------------------------------------------ */
1173    /**
1174     * @return Returns the connectionsOpenMin.
1175     */

1176    public int getConnectionsOpenMin()
1177    {
1178        return _connectionsOpenMin;
1179    }
1180
1181    /* ------------------------------------------------------------ */
1182    /**
1183     * @return Returns the connectionsRequestsMin.
1184     */

1185    public int getConnectionsRequestsMin()
1186    {
1187        return _connectionsRequestsMin;
1188    }
1189
1190    /* ------------------------------------------------------------ */
1191    /**
1192     * @return Returns the requestsActiveMin.
1193     */

1194    public int getRequestsActiveMin()
1195    {
1196        return _requestsActiveMin;
1197    }
1198
1199    /* ------------------------------------------------------------ */
1200    /**
1201     * @return Returns the requestsDurationMin.
1202     */

1203    public long getRequestsDurationMin()
1204    {
1205        return _requestsDurationMin;
1206    }
1207
1208    /* ------------------------------------------------------------ */
1209    /**
1210     * @return Returns the requestsDurationTotal.
1211     */

1212    public long getRequestsDurationTotal()
1213    {
1214        return _requestsDurationTotal;
1215    }
1216
1217    /* ------------------------------------------------------------ */
1218    /**
1219     * @return Number of connections accepted by the server since
1220     * statsReset() called. Undefined if setStatsOn(false).
1221     */

1222    public int getConnections() {return _connections;}
1223
1224    /* ------------------------------------------------------------ */
1225    /**
1226     * @return Number of connections currently open that were opened
1227     * since statsReset() called. Undefined if setStatsOn(false).
1228     */

1229    public int getConnectionsOpen() {return _connectionsOpen;}
1230
1231    /* ------------------------------------------------------------ */
1232    /**
1233     * @return Maximum number of connections opened simultaneously
1234     * since statsReset() called. Undefined if setStatsOn(false).
1235     */

1236    public int getConnectionsOpenMax() {return _connectionsOpenMax;}
1237
1238    /* ------------------------------------------------------------ */
1239    /**
1240     * @return Average duration in milliseconds of open connections
1241     * since statsReset() called. Undefined if setStatsOn(false).
1242     */

1243    public long getConnectionsDurationAve() {return _connections==0?0:(_connectionsDurationTotal/_connections);}
1244
1245    /* ------------------------------------------------------------ */
1246    /**
1247     * @return Maximum duration in milliseconds of an open connection
1248     * since statsReset() called. Undefined if setStatsOn(false).
1249     */

1250    public long getConnectionsDurationMax() {return _connectionsDurationMax;}
1251
1252    /* ------------------------------------------------------------ */
1253    /**
1254     * @return Average number of requests per connection
1255     * since statsReset() called. Undefined if setStatsOn(false).
1256     */

1257    public int getConnectionsRequestsAve() {return _connections==0?0:(_requests/_connections);}
1258
1259    /* ------------------------------------------------------------ */
1260    /**
1261     * @return Maximum number of requests per connection
1262     * since statsReset() called. Undefined if setStatsOn(false).
1263     */

1264    public int getConnectionsRequestsMax() {return _connectionsRequestsMax;}
1265
1266
1267    /* ------------------------------------------------------------ */
1268    /**
1269     * @return Number of errors generated while handling requests.
1270     * since statsReset() called. Undefined if setStatsOn(false).
1271     */

1272    public int getErrors() {return _errors;}
1273
1274    /* ------------------------------------------------------------ */
1275    /**
1276     * @return Number of requests
1277     * since statsReset() called. Undefined if setStatsOn(false).
1278     */

1279    public int getRequests() {return _requests;}
1280
1281    /* ------------------------------------------------------------ */
1282    /**
1283     * @return Number of requests currently active.
1284     * Undefined if setStatsOn(false).
1285     */

1286    public int getRequestsActive() {return _requestsActive;}
1287
1288    /* ------------------------------------------------------------ */
1289    /**
1290     * @return Maximum number of active requests
1291     * since statsReset() called. Undefined if setStatsOn(false).
1292     */

1293    public int getRequestsActiveMax() {return _requestsActiveMax;}
1294
1295    /* ------------------------------------------------------------ */
1296    /**
1297     * @return Average duration of request handling in milliseconds
1298     * since statsReset() called. Undefined if setStatsOn(false).
1299     */

1300    public long getRequestsDurationAve() {return _requests==0?0:(_requestsDurationTotal/_requests);}
1301
1302    /* ------------------------------------------------------------ */
1303    /**
1304     * @return Get maximum duration in milliseconds of request handling
1305     * since statsReset() called. Undefined if setStatsOn(false).
1306     */

1307    public long getRequestsDurationMax() {return _requestsDurationMax;}
1308    
1309    /* ------------------------------------------------------------ */
1310    void statsOpenConnection()
1311    {
1312        synchronized(_statsLock)
1313        {
1314            _connectionsOpen++;
1315            if (_connectionsOpen > _connectionsOpenMax)
1316                _connectionsOpenMax=_connectionsOpen;
1317        }
1318    }
1319    
1320    /* ------------------------------------------------------------ */
1321    void statsGotRequest()
1322    {
1323        synchronized(_statsLock)
1324        {
1325            _requestsActive++;
1326            if (_requestsActive > _requestsActiveMax)
1327                _requestsActiveMax=_requestsActive;
1328        }
1329    }
1330    
1331    /* ------------------------------------------------------------ */
1332    void statsEndRequest(long duration,boolean ok)
1333    {
1334        synchronized(_statsLock)
1335        {
1336            _requests++;
1337            _requestsActive--;
1338            if (_requestsActive<0)
1339                _requestsActive=0;
1340            if (_requestsActive < _requestsActiveMin)
1341                _requestsActiveMin=_requestsActive;
1342            
1343            if (ok)
1344            {
1345                _requestsDurationTotal+=duration;
1346                if (_requestsDurationMin==0 || duration<_requestsDurationMin)
1347                    _requestsDurationMin=duration;
1348                if (duration>_requestsDurationMax)
1349                    _requestsDurationMax=duration;
1350            }
1351            else
1352                _errors++;
1353        }
1354    }
1355    
1356    /* ------------------------------------------------------------ */
1357    void statsCloseConnection(long duration,int requests)
1358    {
1359        synchronized(_statsLock)
1360        {
1361            _connections++;
1362            _connectionsOpen--;
1363            _connectionsDurationTotal+=duration;
1364            if (_connectionsOpen<0)
1365                _connectionsOpen=0;
1366            if (_connectionsOpen<_connectionsOpenMin)
1367                _connectionsOpenMin=_connectionsOpen;
1368            if (_connectionsDurationMin==0 || duration<_connectionsDurationMin)
1369                _connectionsDurationMin=duration;
1370            if (duration>_connectionsDurationMax)
1371                _connectionsDurationMax=duration;
1372            if (_connectionsRequestsMin==0 || requests<_connectionsRequestsMin)
1373                _connectionsRequestsMin=requests;
1374            if (requests>_connectionsRequestsMax)
1375                _connectionsRequestsMax=requests;
1376        }
1377    }
1378
1379
1380    /* ------------------------------------------------------------ */
1381    /** Save the HttpServer
1382     * The server is saved by serialization to the given filename or URL.
1383     *
1384     * @param saveat A file or URL to save the configuration at.
1385     * @exception MalformedURLException
1386     * @exception IOException
1387     */

1388    public void save(String JavaDoc saveat)
1389        throws MalformedURLException JavaDoc,
1390               IOException JavaDoc
1391    {
1392        Resource resource = Resource.newResource(saveat);
1393        ObjectOutputStream JavaDoc out = new ObjectOutputStream JavaDoc(resource.getOutputStream());
1394        out.writeObject(this);
1395        out.flush();
1396        out.close();
1397        log.info("Saved "+this+" to "+resource);
1398    }
1399    
1400    /* ------------------------------------------------------------ */
1401    /** Destroy a stopped server.
1402     * Remove all components and send notifications to all event
1403     * listeners. The HttpServer must be stopped before it can be destroyed.
1404     */

1405    public void destroy()
1406    {
1407        __servers.remove(this);
1408        if (isStarted())
1409            throw new IllegalStateException JavaDoc("Started");
1410        if (_listeners!=null)
1411            _listeners.clear();
1412        _listeners=null;
1413        if (_virtualHostMap!=null)
1414            _virtualHostMap.clear();
1415        _virtualHostMap=null;
1416        _notFoundContext=null;
1417
1418        super.destroy();
1419    }
1420    
1421    /* ------------------------------------------------------------ */
1422    /* ------------------------------------------------------------ */
1423    /** Construct server from command line arguments.
1424     * @param args
1425     */

1426    public static void main(String JavaDoc[] args)
1427    {
1428        if (args.length==0 || args.length>2)
1429        {
1430            System.err.println
1431                ("\nUsage - java org.mortbay.http.HttpServer [<addr>:]<port>");
1432            System.err.println
1433                ("\nUsage - java org.mortbay.http.HttpServer -r [savefile]");
1434            System.err.println
1435                (" Serves files from '.' directory");
1436            System.err.println
1437                (" Dump handler for not found requests");
1438            System.err.println
1439                (" Default port is 8080");
1440            System.exit(1);
1441        }
1442        
1443        try{
1444            
1445            if (args.length==1)
1446            {
1447                // Create the server
1448
HttpServer server = new HttpServer();
1449                
1450                // Default is no virtual host
1451
String JavaDoc host=null;
1452                HttpContext context = server.getContext(host,"/");
1453                context.setResourceBase(".");
1454                context.addHandler(new ResourceHandler());
1455                context.addHandler(new DumpHandler());
1456                context.addHandler(new NotFoundHandler());
1457
1458                InetAddrPort address = new InetAddrPort(args[0]);
1459                server.addListener(address);
1460
1461                server.start();
1462            }
1463            else
1464            {
1465                Resource resource = Resource.newResource(args[1]);
1466                ObjectInputStream JavaDoc in = new ObjectInputStream JavaDoc(resource.getInputStream());
1467                HttpServer server = (HttpServer)in.readObject();
1468                in.close();
1469                server.start();
1470            }
1471            
1472        }
1473        catch (Exception JavaDoc e)
1474        {
1475            log.warn(LogSupport.EXCEPTION,e);
1476        }
1477    }
1478}
1479
Popular Tags