KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > jk > server > JkCoyoteHandler


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.jk.server;
18
19 import java.io.ByteArrayInputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.net.InetAddress JavaDoc;
22 import java.security.cert.CertificateFactory JavaDoc;
23 import java.security.cert.X509Certificate JavaDoc;
24 import java.security.PrivilegedExceptionAction JavaDoc;
25 import java.security.AccessController JavaDoc;
26 import java.security.PrivilegedActionException JavaDoc;
27 import java.security.PrivilegedAction JavaDoc;
28 import java.util.Iterator JavaDoc;
29
30 import javax.management.MBeanServer JavaDoc;
31 import javax.management.ObjectName JavaDoc;
32
33 import org.apache.commons.modeler.Registry;
34 import org.apache.coyote.ActionCode;
35 import org.apache.coyote.ActionHook;
36 import org.apache.coyote.Adapter;
37 import org.apache.coyote.ProtocolHandler;
38 import org.apache.coyote.Request;
39 import org.apache.coyote.Response;
40 import org.apache.coyote.RequestInfo;
41 import org.apache.coyote.Constants;
42 import org.apache.jk.common.HandlerRequest;
43 import org.apache.jk.common.JkInputStream;
44 import org.apache.jk.common.MsgAjp;
45 import org.apache.jk.core.JkHandler;
46 import org.apache.jk.core.Msg;
47 import org.apache.jk.core.MsgContext;
48 import org.apache.jk.core.WorkerEnv;
49 import org.apache.tomcat.util.buf.ByteChunk;
50 import org.apache.tomcat.util.buf.C2BConverter;
51 import org.apache.tomcat.util.buf.MessageBytes;
52 import org.apache.tomcat.util.http.HttpMessages;
53 import org.apache.tomcat.util.http.MimeHeaders;
54 import org.apache.tomcat.util.net.SSLSupport;
55
56 /** Plugs Jk into Coyote. Must be named "type=JkHandler,name=container"
57  *
58  * @jmx:notification-handler name="org.apache.jk.SEND_PACKET
59  * @jmx:notification-handler name="org.apache.coyote.ACTION_COMMIT
60  */

61 public class JkCoyoteHandler extends JkHandler implements
62     ProtocolHandler,
63     ActionHook,
64     org.apache.coyote.OutputBuffer,
65     org.apache.coyote.InputBuffer
66 {
67     protected static org.apache.commons.logging.Log log
68         = org.apache.commons.logging.LogFactory.getLog(JkCoyoteHandler.class);
69     // Set debug on this logger to see the container request time
70
private static org.apache.commons.logging.Log logTime=
71         org.apache.commons.logging.LogFactory.getLog( "org.apache.jk.REQ_TIME" );
72
73     // ----------------------------------------------------------- DoPrivileged
74
private final class StatusLinePrivilegedAction implements PrivilegedAction JavaDoc {
75     int status;
76     StatusLinePrivilegedAction(int status) {
77         this.status = status;
78     }
79     public Object JavaDoc run() {
80         return HttpMessages.getMessage(status);
81     }
82     }
83
84     int headersMsgNote;
85     int c2bConvertersNote;
86     int tmpMessageBytesNote;
87     int utfC2bNote;
88     int obNote;
89     int epNote;
90     int inputStreamNote;
91     private boolean paused = false;
92     
93     Adapter adapter;
94     protected JkMain jkMain=null;
95
96     public final int JK_STATUS_NEW=0;
97     public final int JK_STATUS_HEAD=1;
98     public final int JK_STATUS_CLOSED=2;
99     public final int JK_STATUS_ERROR=3;
100
101     /** Set a property. Name is a "component.property". JMX should
102      * be used instead.
103      */

104     public void setProperty( String JavaDoc name, String JavaDoc value ) {
105         if( log.isTraceEnabled())
106             log.trace("setProperty " + name + " " + value );
107         getJkMain().setProperty( name, value );
108         properties.put( name, value );
109     }
110
111     public String JavaDoc getProperty( String JavaDoc name ) {
112         return properties.getProperty(name) ;
113     }
114
115     public Iterator JavaDoc getAttributeNames() {
116        return properties.keySet().iterator();
117     }
118
119     /** Pass config info
120      */

121     public void setAttribute( String JavaDoc name, Object JavaDoc value ) {
122         if( log.isDebugEnabled())
123             log.debug("setAttribute " + name + " " + value );
124         if( value instanceof String JavaDoc )
125             this.setProperty( name, (String JavaDoc)value );
126     }
127
128     /**
129      * Retrieve config info.
130      * Primarily for use with the admin webapp.
131      */

132     public Object JavaDoc getAttribute( String JavaDoc name ) {
133         return getJkMain().getProperty(name);
134     }
135
136     /** The adapter, used to call the connector
137      */

138     public void setAdapter(Adapter adapter) {
139         this.adapter=adapter;
140     }
141
142     public Adapter getAdapter() {
143         return adapter;
144     }
145
146     public JkMain getJkMain() {
147         if( jkMain == null ) {
148             jkMain=new JkMain();
149             jkMain.setWorkerEnv(wEnv);
150             
151         }
152         return jkMain;
153     }
154     
155     boolean started=false;
156     
157     /** Start the protocol
158      */

159     public void init() {
160         if( started ) return;
161
162         started=true;
163         
164         if( wEnv==null ) {
165             // we are probably not registered - not very good.
166
wEnv=getJkMain().getWorkerEnv();
167             wEnv.addHandler("container", this );
168         }
169
170         try {
171             // jkMain.setJkHome() XXX;
172

173             getJkMain().init();
174
175             headersMsgNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "headerMsg" );
176             tmpMessageBytesNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "tmpMessageBytes" );
177             utfC2bNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "utfC2B" );
178             epNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "ep" );
179             obNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "coyoteBuffer" );
180             inputStreamNote= wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE,
181                                              "jkInputStream");
182
183         } catch( Exception JavaDoc ex ) {
184             log.error("Error during init",ex);
185         }
186     }
187
188     public void start() {
189         try {
190             if( oname != null && getJkMain().getDomain() == null) {
191                 try {
192                     ObjectName JavaDoc jkmainOname =
193                         new ObjectName JavaDoc(oname.getDomain() + ":type=JkMain");
194                     Registry.getRegistry(null, null)
195                         .registerComponent(getJkMain(), jkmainOname, "JkMain");
196                 } catch (Exception JavaDoc e) {
197                     log.error( "Error registering jkmain " + e );
198                 }
199             }
200             getJkMain().start();
201         } catch( Exception JavaDoc ex ) {
202             log.error("Error during startup",ex);
203         }
204     }
205
206     public void pause() throws Exception JavaDoc {
207         if(!paused) {
208             paused = true;
209             getJkMain().pause();
210         }
211     }
212
213     public void resume() throws Exception JavaDoc {
214         if(paused) {
215             paused = false;
216             getJkMain().resume();
217         }
218     }
219
220     public void destroy() {
221         if( !started ) return;
222
223         started = false;
224         getJkMain().stop();
225     }
226
227     // -------------------- OutputBuffer implementation --------------------
228

229         
230     public int doWrite(ByteChunk chunk, Response res)
231         throws IOException JavaDoc
232     {
233         if (!res.isCommitted()) {
234             // Send the connector a request for commit. The connector should
235
// then validate the headers, send them (using sendHeader) and
236
// set the filters accordingly.
237
res.sendHeaders();
238         }
239         MsgContext ep=(MsgContext)res.getNote( epNote );
240
241         MsgAjp msg=(MsgAjp)ep.getNote( headersMsgNote );
242
243         int len=chunk.getLength();
244         byte buf[]=msg.getBuffer();
245         // 4 - hardcoded, byte[] marshalling overhead
246
int chunkSize=buf.length - msg.getHeaderLength() - 4;
247         int off=0;
248         while( len > 0 ) {
249             int thisTime=len;
250             if( thisTime > chunkSize ) {
251                 thisTime=chunkSize;
252             }
253             len-=thisTime;
254             
255             msg.reset();
256             msg.appendByte( HandlerRequest.JK_AJP13_SEND_BODY_CHUNK);
257             if( log.isDebugEnabled() ) log.debug("doWrite " + off + " " + thisTime + " " + len );
258             msg.appendBytes( chunk.getBytes(), chunk.getOffset() + off, thisTime );
259             off+=thisTime;
260             ep.setType( JkHandler.HANDLE_SEND_PACKET );
261             ep.getSource().send( msg, ep );
262         }
263         return 0;
264     }
265     
266     public int doRead(ByteChunk chunk, Request req)
267         throws IOException JavaDoc
268     {
269         Response res=req.getResponse();
270         if( log.isDebugEnabled() )
271             log.debug("doRead " + chunk.getBytes() + " " + chunk.getOffset() + " " + chunk.getLength());
272         MsgContext ep=(MsgContext)res.getNote( epNote );
273         
274         JkInputStream jkIS=(JkInputStream)ep.getNote( inputStreamNote );
275         // return jkIS.read( chunk.getBytes(), chunk.getOffset(), chunk.getLength());
276
return jkIS.doRead( chunk );
277     }
278     
279     // -------------------- Jk handler implementation --------------------
280
// Jk Handler mehod
281
public int invoke( Msg msg, MsgContext ep )
282         throws IOException JavaDoc
283     {
284         if( logTime.isDebugEnabled() )
285                 ep.setLong( MsgContext.TIMER_PRE_REQUEST, System.currentTimeMillis());
286         
287         org.apache.coyote.Request req=(org.apache.coyote.Request)ep.getRequest();
288         org.apache.coyote.Response res=req.getResponse();
289         res.setHook( this );
290
291         if( log.isDebugEnabled() )
292             log.debug( "Invoke " + req + " " + res + " " + req.requestURI().toString());
293         
294         res.setOutputBuffer( this );
295         req.setInputBuffer( this );
296         
297         if( ep.getNote( headersMsgNote ) == null ) {
298             Msg msg2=new MsgAjp();
299             ep.setNote( headersMsgNote, msg2 );
300         }
301         
302         res.setNote( epNote, ep );
303         ep.setStatus( JK_STATUS_HEAD );
304     RequestInfo rp = req.getRequestProcessor();
305     rp.setStage(Constants.STAGE_SERVICE);
306         try {
307             adapter.service( req, res );
308         } catch( Exception JavaDoc ex ) {
309             log.info("Error servicing request " + req,ex);
310         }
311         if(ep.getStatus() != JK_STATUS_CLOSED) {
312             res.finish();
313         }
314
315         req.recycle();
316         req.updateCounters();
317         res.recycle();
318         if( ep.getStatus() == JK_STATUS_ERROR ) {
319             return ERROR;
320         }
321         ep.setStatus( JK_STATUS_NEW );
322     rp.setStage(Constants.STAGE_KEEPALIVE);
323         return OK;
324     }
325
326     private void appendHead(org.apache.coyote.Response res)
327         throws IOException JavaDoc
328     {
329         if( log.isDebugEnabled() )
330             log.debug("COMMIT sending headers " + res + " " + res.getMimeHeaders() );
331         
332         C2BConverter c2b=(C2BConverter)res.getNote( utfC2bNote );
333         if( c2b==null ) {
334             if(System.getSecurityManager() != null) {
335                 try {
336                     c2b = (C2BConverter)
337                         AccessController.doPrivileged(
338                               new PrivilegedExceptionAction JavaDoc () {
339                                       public Object JavaDoc run()
340                                           throws IOException JavaDoc{
341                                           return new C2BConverter( "iso-8859-1" );
342                                       }
343                                   });
344                 } catch(PrivilegedActionException JavaDoc pae) {
345                     Exception JavaDoc ex = pae.getException();
346                     if(ex instanceof IOException JavaDoc)
347                         throw (IOException JavaDoc)ex;
348                 }
349             } else {
350                 c2b=new C2BConverter( "iso-8859-1" );
351         }
352             res.setNote( utfC2bNote, c2b );
353         }
354         
355         MsgContext ep=(MsgContext)res.getNote( epNote );
356         MsgAjp msg=(MsgAjp)ep.getNote( headersMsgNote );
357         msg.reset();
358         msg.appendByte(HandlerRequest.JK_AJP13_SEND_HEADERS);
359         msg.appendInt( res.getStatus() );
360         
361         MessageBytes mb=(MessageBytes)ep.getNote( tmpMessageBytesNote );
362         if( mb==null ) {
363             mb=MessageBytes.newInstance();
364             ep.setNote( tmpMessageBytesNote, mb );
365         }
366         String JavaDoc message=res.getMessage();
367         if( message==null ){
368         if( System.getSecurityManager() != null ) {
369         message = (String JavaDoc)AccessController.doPrivileged(
370                             new StatusLinePrivilegedAction(res.getStatus()));
371         } else {
372         message= HttpMessages.getMessage(res.getStatus());
373         }
374         } else {
375             message = message.replace('\n', ' ').replace('\r', ' ');
376         }
377         mb.setString( message );
378         c2b.convert( mb );
379         msg.appendBytes(mb);
380
381         // XXX add headers
382

383         MimeHeaders headers=res.getMimeHeaders();
384         String JavaDoc contentType = res.getContentType();
385         if( contentType != null ) {
386             headers.setValue("Content-Type").setString(contentType);
387         }
388         String JavaDoc contentLanguage = res.getContentLanguage();
389         if( contentLanguage != null ) {
390             headers.setValue("Content-Language").setString(contentLanguage);
391         }
392     int contentLength = res.getContentLength();
393         if( contentLength >= 0 ) {
394             headers.setValue("Content-Length").setInt(contentLength);
395         }
396         int numHeaders = headers.size();
397         msg.appendInt(numHeaders);
398         for( int i=0; i<numHeaders; i++ ) {
399             MessageBytes hN=headers.getName(i);
400             // no header to sc conversion - there's little benefit
401
// on this direction
402
c2b.convert ( hN );
403             msg.appendBytes( hN );
404                         
405             MessageBytes hV=headers.getValue(i);
406             c2b.convert( hV );
407             msg.appendBytes( hV );
408         }
409         ep.setType( JkHandler.HANDLE_SEND_PACKET );
410         ep.getSource().send( msg, ep );
411     }
412     
413     // -------------------- Coyote Action implementation --------------------
414

415     public void action(ActionCode actionCode, Object JavaDoc param) {
416         if( actionCode==ActionCode.ACTION_COMMIT ) {
417             if( log.isDebugEnabled() ) log.debug("COMMIT " );
418             org.apache.coyote.Response res=(org.apache.coyote.Response)param;
419
420             if( res.isCommitted() ) {
421                 if( log.isInfoEnabled() )
422                     log.info("Response already committed " );
423             } else {
424                 try {
425                     appendHead( res );
426                 } catch(IOException JavaDoc iex) {
427                     log.warn("Unable to send headers",iex);
428                     MsgContext ep=(MsgContext)res.getNote( epNote );
429                     ep.setStatus(JK_STATUS_ERROR);
430                 }
431             }
432         } else if( actionCode==ActionCode.ACTION_RESET ) {
433             if( log.isDebugEnabled() )
434                 log.debug("RESET " );
435             
436         } else if( actionCode==ActionCode.ACTION_CLIENT_FLUSH ) {
437             if( log.isDebugEnabled() ) log.debug("CLIENT_FLUSH " );
438             org.apache.coyote.Response res=(org.apache.coyote.Response)param;
439             MsgContext ep=(MsgContext)res.getNote( epNote );
440             ep.setType( JkHandler.HANDLE_FLUSH );
441             try {
442                 ep.getSource().flush( null, ep );
443             } catch(IOException JavaDoc iex) {
444                 // This is logged elsewhere, so debug only here
445
log.debug("Error during flush",iex);
446                 res.setErrorException(iex);
447                 ep.setStatus(JK_STATUS_ERROR);
448             }
449             
450         } else if( actionCode==ActionCode.ACTION_CLOSE ) {
451             if( log.isDebugEnabled() ) log.debug("CLOSE " );
452             
453             org.apache.coyote.Response res=(org.apache.coyote.Response)param;
454             MsgContext ep=(MsgContext)res.getNote( epNote );
455             if( ep.getStatus()== JK_STATUS_CLOSED ) {
456                 // Double close - it may happen with forward
457
if( log.isDebugEnabled() ) log.debug("Double CLOSE - forward ? " + res.getRequest().requestURI() );
458                 return;
459             }
460                  
461             if( !res.isCommitted() )
462                 this.action( ActionCode.ACTION_COMMIT, param );
463             
464             MsgAjp msg=(MsgAjp)ep.getNote( headersMsgNote );
465             msg.reset();
466             msg.appendByte( HandlerRequest.JK_AJP13_END_RESPONSE );
467             msg.appendByte( 1 );
468
469             try {
470                 ep.setType( JkHandler.HANDLE_SEND_PACKET );
471                 ep.getSource().send( msg, ep );
472                 
473                 ep.setType( JkHandler.HANDLE_FLUSH );
474                 ep.getSource().flush( msg, ep );
475             } catch(IOException JavaDoc iex) {
476                 log.debug("Connection error ending request.",iex);
477                 ep.setStatus(JK_STATUS_ERROR);
478             }
479             if(ep.getStatus() != JK_STATUS_ERROR) {
480                 ep.setStatus(JK_STATUS_CLOSED );
481             }
482
483             if( logTime.isDebugEnabled() )
484                 logTime(res.getRequest(), res);
485         } else if( actionCode==ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
486             org.apache.coyote.Request req=(org.apache.coyote.Request)param;
487
488             // Extract SSL certificate information (if requested)
489
MessageBytes certString = (MessageBytes)req.getNote(WorkerEnv.SSL_CERT_NOTE);
490             if( certString != null && !certString.isNull() ) {
491                 ByteChunk certData = certString.getByteChunk();
492                 ByteArrayInputStream JavaDoc bais =
493                     new ByteArrayInputStream JavaDoc(certData.getBytes(),
494                                              certData.getStart(),
495                                              certData.getLength());
496  
497                 // Fill the first element.
498
X509Certificate JavaDoc jsseCerts[] = null;
499                 try {
500                     CertificateFactory JavaDoc cf =
501                         CertificateFactory.getInstance("X.509");
502                     X509Certificate JavaDoc cert = (X509Certificate JavaDoc)
503                         cf.generateCertificate(bais);
504                     jsseCerts = new X509Certificate JavaDoc[1];
505                     jsseCerts[0] = cert;
506                 } catch(java.security.cert.CertificateException JavaDoc e) {
507                     log.error("Certificate convertion failed" , e );
508                     return;
509                 }
510  
511                 req.setAttribute(SSLSupport.CERTIFICATE_KEY,
512                                  jsseCerts);
513             }
514                 
515         } else if( actionCode==ActionCode.ACTION_REQ_HOST_ATTRIBUTE ) {
516             org.apache.coyote.Request req=(org.apache.coyote.Request)param;
517
518             // If remoteHost not set by JK, get it's name from it's remoteAddr
519
if( req.remoteHost().isNull()) {
520                 try {
521                     req.remoteHost().setString(InetAddress.getByName(
522                                                req.remoteAddr().toString()).
523                                                getHostName());
524                 } catch(IOException JavaDoc iex) {
525                     if(log.isDebugEnabled())
526                         log.debug("Unable to resolve "+req.remoteAddr());
527                 }
528             }
529         // } else if( actionCode==ActionCode.ACTION_POST_REQUEST ) {
530

531         } else if( actionCode==ActionCode.ACTION_ACK ) {
532             if( log.isDebugEnabled() )
533                 log.debug("ACK " );
534             // What should we do here ? Who calls it ?
535
}
536     }
537
538     private void logTime(Request req, Response res ) {
539         // called after the request
540
// org.apache.coyote.Request req=(org.apache.coyote.Request)param;
541
// Response res=req.getResponse();
542
MsgContext ep=(MsgContext)res.getNote( epNote );
543         String JavaDoc uri=req.requestURI().toString();
544         if( uri.indexOf( ".gif" ) >0 ) return;
545         
546         ep.setLong( MsgContext.TIMER_POST_REQUEST, System.currentTimeMillis());
547         long t1= ep.getLong( MsgContext.TIMER_PRE_REQUEST ) -
548             ep.getLong( MsgContext.TIMER_RECEIVED );
549         long t2= ep.getLong( MsgContext.TIMER_POST_REQUEST ) -
550             ep.getLong( MsgContext.TIMER_PRE_REQUEST );
551         
552         logTime.debug("Time pre=" + t1 + "/ service=" + t2 + " " +
553                       res.getContentLength() + " " +
554                       uri );
555     }
556
557     public ObjectName JavaDoc preRegister(MBeanServer JavaDoc server,
558                                   ObjectName JavaDoc oname) throws Exception JavaDoc
559     {
560         // override - we must be registered as "container"
561
this.name="container";
562         return super.preRegister(server, oname);
563     }
564 }
565
Popular Tags