~ubuntu-branches/ubuntu/raring/libjboss-remoting-java/raring

« back to all changes in this revision

Viewing changes to src/main/org/jboss/remoting/transport/coyote/CoyoteInvoker.java

  • Committer: Package Import Robot
  • Author(s): Torsten Werner
  • Date: 2011-09-09 14:01:03 UTC
  • mto: This revision was merged to the branch mainline in revision 9.
  • Revision ID: package-import@ubuntu.com-20110909140103-o8ucrolqt5g25k57
Tags: upstream-2.5.3.SP1
ImportĀ upstreamĀ versionĀ 2.5.3.SP1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
18
package org.jboss.remoting.transport.coyote;
 
19
 
 
20
import org.apache.coyote.ActionCode;
 
21
import org.apache.coyote.Adapter;
 
22
import org.apache.coyote.ProtocolHandler;
 
23
import org.apache.coyote.Request;
 
24
import org.apache.coyote.Response;
 
25
import org.apache.tomcat.util.buf.B2CConverter;
 
26
import org.apache.tomcat.util.buf.ByteChunk;
 
27
import org.apache.tomcat.util.buf.CharChunk;
 
28
import org.apache.tomcat.util.buf.MessageBytes;
 
29
import org.apache.tomcat.util.http.MimeHeaders;
 
30
import org.apache.tomcat.util.net.SocketStatus;
 
31
import org.jboss.remoting.Home;
 
32
import org.jboss.remoting.InvocationRequest;
 
33
import org.jboss.remoting.InvocationResponse;
 
34
import org.jboss.remoting.InvokerLocator;
 
35
import org.jboss.remoting.Remoting;
 
36
import org.jboss.remoting.Version;
 
37
import org.jboss.remoting.marshal.Marshaller;
 
38
import org.jboss.remoting.marshal.UnMarshaller;
 
39
import org.jboss.remoting.marshal.VersionedMarshaller;
 
40
import org.jboss.remoting.marshal.VersionedUnMarshaller;
 
41
import org.jboss.remoting.marshal.http.HTTPUnMarshaller;
 
42
import org.jboss.remoting.security.SSLSocketBuilder;
 
43
import org.jboss.remoting.transport.coyote.ssl.RemotingSSLImplementation;
 
44
import org.jboss.remoting.transport.coyote.ssl.RemotingServerSocketFactory;
 
45
import org.jboss.remoting.transport.http.HTTPMetadataConstants;
 
46
import org.jboss.remoting.transport.web.WebServerInvoker;
 
47
import org.jboss.remoting.transport.web.WebUtil;
 
48
import org.jboss.remoting.util.SecurityUtility;
 
49
import org.jboss.logging.Logger;
 
50
 
 
51
import javax.net.ServerSocketFactory;
 
52
 
 
53
import java.io.IOException;
 
54
import java.lang.reflect.Method;
 
55
import java.net.InetAddress;
 
56
import java.net.UnknownHostException;
 
57
import java.security.AccessController;
 
58
import java.security.PrivilegedAction;
 
59
import java.security.PrivilegedActionException;
 
60
import java.security.PrivilegedExceptionAction;
 
61
import java.util.ArrayList;
 
62
import java.util.Enumeration;
 
63
import java.util.HashMap;
 
64
import java.util.Iterator;
 
65
import java.util.List;
 
66
import java.util.Map;
 
67
 
 
68
/**
 
69
 * This is the stand alone http server invoker which acts basically as a web server.
 
70
 * Server invoker implementation based on http protocol.  Is basically a stand alone http server whose request are
 
71
 * forwared to the invocation handler and responses from invocation handler are sent back to caller as http response.
 
72
 *
 
73
 * @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
 
74
 * @author Craig R. McClanahan
 
75
 * @author Remy Maucherat
 
76
 */
 
77
 
 
78
/**
 
79
 * Some of the code in this class was pulled from org.apache.coyote.tomcat4.CoyoteAdapter
 
80
 * and hence will maintain the Apache License (and author credit from original source).
 
81
 */
 
82
public class CoyoteInvoker extends WebServerInvoker implements Adapter
 
83
{
 
84
   private static final Logger log = Logger.getLogger(CoyoteInvoker.class);
 
85
 
 
86
   /** Indicates if input was raw or an InvocationRequest */
 
87
   protected static ThreadLocal receivedInvocationRequest = new ThreadLocal();
 
88
   protected static final Boolean FALSE = new Boolean(false);
 
89
   protected static final Boolean TRUE = new Boolean(true);
 
90
 
 
91
   private boolean running = false;
 
92
 
 
93
//   protected ProtocolHandler protocolHandler = null;
 
94
   protected List protocolHandlers = new ArrayList();
 
95
 
 
96
   protected String URIEncoding = null;
 
97
   
 
98
   protected String useRemotingContentType = "false";
 
99
 
 
100
 
 
101
   public CoyoteInvoker(InvokerLocator locator)
 
102
   {
 
103
      super(locator);
 
104
   }
 
105
 
 
106
   public CoyoteInvoker(InvokerLocator locator, Map configuration)
 
107
   {
 
108
      super(locator, configuration);
 
109
   }
 
110
 
 
111
   protected void setup() throws Exception
 
112
   {
 
113
 
 
114
      super.setup();
 
115
 
 
116
      Map config = getConfiguration();
 
117
 
 
118
      // Test APR support
 
119
      boolean apr = false;
 
120
      try
 
121
      {
 
122
         AccessController.doPrivileged( new PrivilegedExceptionAction()
 
123
         {
 
124
            public Object run() throws Exception
 
125
            {
 
126
               String methodName = "initialize";
 
127
               Class paramTypes[] = new Class[1];
 
128
               paramTypes[0] = String.class;
 
129
               Object paramValues[] = new Object[1];
 
130
               paramValues[0] = null;
 
131
               String className = "org.apache.tomcat.jni.Library";
 
132
               Method method = Class.forName(className).getMethod(methodName, paramTypes);
 
133
               method.invoke(null, paramValues);
 
134
               return null;
 
135
            }
 
136
         });
 
137
         apr = true;
 
138
      }
 
139
      catch (PrivilegedActionException e)
 
140
      {
 
141
         // Ignore.
 
142
         log.trace("", e.getCause());
 
143
      }
 
144
 
 
145
      // Instantiate the associated HTTP protocol handler
 
146
      String protocolHandlerClassName = null;
 
147
      Object value = config.get("protocolHandlerClassName");
 
148
      if(value != null)
 
149
      {
 
150
         protocolHandlerClassName = String.valueOf(value);
 
151
      }
 
152
      else
 
153
      {
 
154
         if(apr)
 
155
         {
 
156
            protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";
 
157
         }
 
158
         else
 
159
         {
 
160
            protocolHandlerClassName = "org.apache.coyote.http11.Http11Protocol";
 
161
         }
 
162
      }
 
163
      log.info("Using " + protocolHandlerClassName + " for http (coyote) invoker protocol handler.");
 
164
 
 
165
      Class clazz = null;
 
166
      try
 
167
      {
 
168
         clazz = (Class) forName(protocolHandlerClassName);
 
169
      }
 
170
      catch (ClassNotFoundException e)
 
171
      {
 
172
         log.error("Protocol handler class instatiation failed: protocolHandlerClassName", e);
 
173
         return;
 
174
      }
 
175
      
 
176
      ProtocolHandler protocolHandler = null;
 
177
      for (int i = 0; i < connectHomes.size(); i++)
 
178
      {
 
179
         try
 
180
         {
 
181
            protocolHandler = (ProtocolHandler) clazz.newInstance();
 
182
         }
 
183
         catch(Exception e)
 
184
         {
 
185
            log.error("Protocol handler instantiation failed", e);
 
186
            return;
 
187
         }
 
188
         protocolHandler.setAdapter(this);
 
189
 
 
190
         // Pass all attributes to the protocol handler
 
191
         Iterator keys = config.keySet().iterator();
 
192
         while(keys.hasNext())
 
193
         {
 
194
            String key = (String) keys.next();
 
195
            Object obj = config.get(key);
 
196
            if (obj instanceof String)
 
197
            {
 
198
               String val = (String) obj;
 
199
               setProperty(protocolHandler, key, val);
 
200
            }
 
201
         }
 
202
 
 
203
         // need to convert standard remoting timeout config to tomcat timeout
 
204
         String timeoutValue = (String)config.get(TIMEOUT);
 
205
         if(timeoutValue != null)
 
206
         {
 
207
            setProperty(protocolHandler, "connectionTimeout", timeoutValue);
 
208
         }
 
209
 
 
210
         // Configuration of URI encoding
 
211
         value = config.get("URIEncoding");
 
212
         if(value != null)
 
213
         {
 
214
            URIEncoding = String.valueOf(value);
 
215
         }
 
216
         
 
217
         protocolHandlers.add(protocolHandler);
 
218
         
 
219
         value = config.get(HTTPMetadataConstants.USE_REMOTING_CONTENT_TYPE);
 
220
         if (value instanceof String)
 
221
         {
 
222
            useRemotingContentType = (String) value;
 
223
         }
 
224
         else if (value != null)
 
225
         {
 
226
            log.warn(HTTPMetadataConstants.USE_REMOTING_CONTENT_TYPE + " value should be a String: " + value);
 
227
         }
 
228
         log.debug(this + " useRemotingContentType: " + useRemotingContentType);
 
229
      }
 
230
   }
 
231
 
 
232
   protected ServerSocketFactory getDefaultServerSocketFactory() throws IOException
 
233
   {
 
234
      /**
 
235
       * Returning a null here as if has not already been set previously
 
236
       * via config in ServerInvoker, then want to return null so that
 
237
       * will use the default within tomcat (and not override with own default).
 
238
       */
 
239
 
 
240
      if ("https".equals(locator.getProtocol()))
 
241
      {
 
242
         SSLSocketBuilder builder = new SSLSocketBuilder(configuration);
 
243
         builder.setUseSSLServerSocketFactory(false);
 
244
         try
 
245
         {
 
246
            return builder.createSSLServerSocketFactory();
 
247
         }
 
248
         catch (IOException e)
 
249
         {
 
250
            log.debug("unable to create server socket factory", e);
 
251
            throw e;
 
252
         }
 
253
      }
 
254
      else
 
255
         return null;
 
256
   }
 
257
 
 
258
   public void start() throws IOException
 
259
   {
 
260
      if(!running)
 
261
      {
 
262
         for (int i = 0; i < protocolHandlers.size(); i++)
 
263
         {
 
264
            try
 
265
            {
 
266
               final ProtocolHandler protocolHandler = (ProtocolHandler) protocolHandlers.get(i);
 
267
               Home home = (Home) getHomes().get(i);
 
268
               setProperty(protocolHandler, "address", home.host);
 
269
               setProperty(protocolHandler, "port", "" + home.port);
 
270
 
 
271
               //TODO: -TME - Should not have to hard set this every time.  Should
 
272
               // be a way to figure out if this is needed or not.
 
273
 
 
274
               // Need to set the MBeanServer to use since there is no direct way to do it.
 
275
               setProperty(protocolHandler, "locator", getLocator().getLocatorURI());
 
276
               RemotingSSLImplementation.setMBeanServer(getLocator().getLocatorURI(), getMBeanServer());
 
277
 
 
278
               ServerSocketFactory svrSocketFactory = getServerSocketFactory();
 
279
               if(svrSocketFactory != null)
 
280
               {
 
281
                  RemotingServerSocketFactory.setServerSocketFactory(getLocator().getLocatorURI(), svrSocketFactory);
 
282
                  setProperty(protocolHandler, "SocketFactory", RemotingServerSocketFactory.class.getName());
 
283
               }
 
284
 
 
285
               try
 
286
               {
 
287
                  AccessController.doPrivileged( new PrivilegedExceptionAction()
 
288
                  {
 
289
                     public Object run() throws Exception
 
290
                     {
 
291
                        protocolHandler.init();
 
292
                        protocolHandler.start();
 
293
                        return null;
 
294
                     }
 
295
                  });
 
296
               }
 
297
               catch (PrivilegedActionException e)
 
298
               {
 
299
                  throw (Exception) e.getCause();
 
300
               }
 
301
 
 
302
               running = true;
 
303
 
 
304
            }
 
305
            catch(Exception e)
 
306
            {
 
307
               log.debug("Error starting protocol handler.  Bind port: " + getServerBindPort() + ", bind address: " + getServerBindAddress(), e);
 
308
               throw new IOException("" + e.getMessage());
 
309
            }
 
310
         }
 
311
      }
 
312
      super.start();
 
313
   }
 
314
 
 
315
   /**
 
316
    * Service method.
 
317
    */
 
318
   public void service(org.apache.coyote.Request req,
 
319
                       org.apache.coyote.Response res)
 
320
         throws Exception
 
321
   {
 
322
 
 
323
      RequestMap request = (RequestMap) req.getNote(1);
 
324
      ResponseMap response = (ResponseMap) res.getNote(1);
 
325
 
 
326
      if(request == null)
 
327
      {
 
328
 
 
329
         // Create objects
 
330
         request = new RequestMap();
 
331
         request.setCoyoteRequest(req);
 
332
         response = new ResponseMap();
 
333
         response.setCoyoteResponse(res);
 
334
 
 
335
         // Set as notes
 
336
         req.setNote(1, request);
 
337
         res.setNote(1, response);
 
338
 
 
339
         // Set query string encoding
 
340
         // FIMXE?: req.getParameters().setQueryStringEncoding(protocolHandler.getAttribute("URIEncoding"));
 
341
 
 
342
      }
 
343
      else
 
344
      {
 
345
         response.clear();
 
346
         request.clear();
 
347
      }
 
348
 
 
349
      try
 
350
      {
 
351
 
 
352
         if(postParseRequest(req, request, res, response))
 
353
         {
 
354
            populateRequestMetadata(request, req);
 
355
 
 
356
            Object responseObject = null;
 
357
            boolean isError = false;
 
358
 
 
359
            int version = getVersion(request);
 
360
 
 
361
            // Check if client is HTTPClientInvoker
 
362
            boolean isRemotingUserAgent = false;
 
363
            Object userAgentObj = request.get(HTTPMetadataConstants.REMOTING_USER_AGENT);
 
364
            if (userAgentObj != null)
 
365
            {
 
366
               String userAgent = (String) userAgentObj;
 
367
               isRemotingUserAgent = userAgent.startsWith("JBossRemoting");
 
368
            }
 
369
 
 
370
            InvocationRequest invocationRequest = versionedRead(req, request, response, version);
 
371
 
 
372
            if (invocationRequest.getRequestPayload() == null)
 
373
               invocationRequest.setRequestPayload(new HashMap());
 
374
            
 
375
            MessageBytes remoteAddressMB = req.remoteAddr();
 
376
            if (remoteAddressMB != null)
 
377
            {
 
378
               String remoteAddressString = remoteAddressMB.toString();
 
379
               InetAddress remoteAddress = getAddressByName(remoteAddressString);
 
380
               invocationRequest.getRequestPayload().put(Remoting.CLIENT_ADDRESS, remoteAddress);  
 
381
            }
 
382
            else
 
383
            {
 
384
               log.debug("unable to retrieve client address from coyote transport layer");
 
385
            }
 
386
            
 
387
            
 
388
            // FIXME: OPTIONS method handling ?
 
389
            try
 
390
            {
 
391
               // call transport on the subclass, get the result to handback
 
392
               responseObject = invoke(invocationRequest);
 
393
            }
 
394
            catch(Throwable ex)
 
395
            {
 
396
               log.debug("Error thrown calling invoke on server invoker.", ex);
 
397
               responseObject = ex;
 
398
               isError = true;
 
399
            }
 
400
 
 
401
            //Start with response code of 204 (no content), then if is a return from handler, change to 200 (ok)
 
402
            int status;
 
403
            String message = "";
 
404
 
 
405
            if(responseObject != null)
 
406
            {
 
407
               if(isError)
 
408
               {
 
409
                  status = 500;
 
410
                  message = "JBoss Remoting: Error occurred within target application.";
 
411
               }
 
412
               else
 
413
               {
 
414
                  status = 200;
 
415
                  message = "OK";
 
416
               }
 
417
            }
 
418
            else
 
419
            {
 
420
               if (isRemotingUserAgent && !req.method().equals("HEAD"))
 
421
               {
 
422
                  status = 200;
 
423
                  message = "OK";
 
424
               }
 
425
               else
 
426
               {
 
427
                  status = 204;
 
428
                  message = "No Content";
 
429
               }
 
430
            }
 
431
 
 
432
            // extract response code/message if exists
 
433
            Map responseMap = invocationRequest.getReturnPayload();
 
434
            if(responseMap != null)
 
435
            {
 
436
               Integer handlerStatus = (Integer) responseMap.get(HTTPMetadataConstants.RESPONSE_CODE);
 
437
               if(handlerStatus != null)
 
438
               {
 
439
                  status = handlerStatus.intValue();
 
440
               }
 
441
               String handlerMessage = (String) responseMap.get(HTTPMetadataConstants.RESPONSE_CODE_MESSAGE);
 
442
               if(handlerMessage != null)
 
443
               {
 
444
                  message = handlerMessage;
 
445
               }
 
446
            }
 
447
            res.setStatus(status);
 
448
            res.setMessage(message);
 
449
 
 
450
            if (isRemotingUserAgent && ((Boolean)receivedInvocationRequest.get()).booleanValue())
 
451
            {
 
452
               responseMap = ((ResponseMap) responseMap).getMap();
 
453
               responseObject = new InvocationResponse(invocationRequest.getSessionId(),
 
454
                                                       responseObject, isError, responseMap);
 
455
            }
 
456
 
 
457
            if(responseObject != null)
 
458
            {
 
459
               versionedWrite(version, responseObject, req, res, response);
 
460
            }
 
461
 
 
462
         }
 
463
 
 
464
         response.outputBuffer.close();
 
465
 
 
466
         req.action(ActionCode.ACTION_POST_REQUEST, null);
 
467
 
 
468
      }
 
469
      catch (ClientAbortException e)
 
470
      {
 
471
         log.debug("Client didn't wait", e);
 
472
      }
 
473
      catch(IOException e)
 
474
      {
 
475
         log.error("Error processing request", e);
 
476
      }
 
477
      catch(Throwable t)
 
478
      {
 
479
         log.error("Service error", t);
 
480
      }
 
481
      finally
 
482
      {
 
483
         // Recycle the wrapper request and response
 
484
         request.recycle();
 
485
         response.recycle();
 
486
      }
 
487
 
 
488
   }
 
489
 
 
490
   private void addLeaseInfo(ResponseMap response)
 
491
   {
 
492
      boolean leaseManagement = isLeaseActivated();
 
493
      response.put("LEASING_ENABLED", new Boolean(leaseManagement));
 
494
 
 
495
      if(leaseManagement)
 
496
      {
 
497
         long leasePeriod = getLeasePeriod();
 
498
         response.put("LEASE_PERIOD", new Long(leasePeriod));
 
499
      }
 
500
   }
 
501
 
 
502
   private void versionedWrite(int version, Object responseObject, Request req, org.apache.coyote.Response res, ResponseMap response)
 
503
         throws IOException
 
504
   {
 
505
      switch (version)
 
506
      {
 
507
         case Version.VERSION_1:
 
508
         case Version.VERSION_2:
 
509
         case Version.VERSION_2_2:
 
510
         {
 
511
            String responseContentType = (String) response.get("Content-Type");
 
512
            if (responseContentType != null)
 
513
            {
 
514
               if (isInvalidContentType(responseContentType))
 
515
               {
 
516
                  log.warn("Ignoring invalid content-type from ServerInvocationHandler: " + responseContentType);
 
517
                  if (responseObject == null)
 
518
                  {
 
519
                     responseContentType = req.getContentType();
 
520
                     if (isInvalidContentType(responseContentType))
 
521
                     {
 
522
                        log.warn("Ignoring invalid content-type from request: " + responseContentType);
 
523
                        responseContentType = WebUtil.getContentType(responseObject); 
 
524
                     }
 
525
                  }
 
526
                  else
 
527
                  {
 
528
                     responseContentType = WebUtil.getContentType(responseObject); 
 
529
                  }
 
530
               }
 
531
            }
 
532
            else
 
533
            {
 
534
               if (responseObject == null)
 
535
               {
 
536
                  responseContentType = req.getContentType();
 
537
                  if (isInvalidContentType(responseContentType))
 
538
                  {
 
539
                     log.warn("Ignoring invalid content-type from request: " + responseContentType);
 
540
                     responseContentType = WebUtil.getContentType(responseObject); 
 
541
                  }
 
542
               }
 
543
               else
 
544
               {
 
545
                  responseContentType = WebUtil.getContentType(responseObject); 
 
546
               }
 
547
            }
 
548
            res.setContentType(responseContentType);
 
549
            
 
550
            if (responseObject instanceof String)
 
551
            {
 
552
               res.addHeader(HTTPMetadataConstants.REMOTING_CONTENT_TYPE, HTTPMetadataConstants.REMOTING_CONTENT_TYPE_STRING);
 
553
            }
 
554
            else
 
555
            {
 
556
               res.addHeader(HTTPMetadataConstants.REMOTING_CONTENT_TYPE, HTTPMetadataConstants.REMOTING_CONTENT_TYPE_NON_STRING);        
 
557
            }
 
558
            
 
559
            Marshaller marshaller = getMarshaller();
 
560
            if (marshaller instanceof VersionedMarshaller)
 
561
               ((VersionedMarshaller) marshaller).write(responseObject, response.getOutputStream(), version);
 
562
            else
 
563
               marshaller.write(responseObject, response.getOutputStream());
 
564
            return;
 
565
         }
 
566
         default:
 
567
         {
 
568
            throw new IOException("Can not send response due to version (" + version + ") not being supported.  Supported versions: " + Version.VERSION_1 + ", " + Version.VERSION_2 + ", " + Version.VERSION_2_2);
 
569
         }
 
570
      }
 
571
   }
 
572
 
 
573
   private InvocationRequest versionedRead(Request req, RequestMap request, ResponseMap response, int version)
 
574
         throws IOException, ClassNotFoundException
 
575
   {
 
576
      switch (version)
 
577
      {
 
578
         case Version.VERSION_1:
 
579
         case Version.VERSION_2:
 
580
         case Version.VERSION_2_2:
 
581
         {
 
582
            // UnMarshaller may not be an HTTPUnMarshaller, in which case it
 
583
            // can ignore this parameter.
 
584
            Object o = configuration.get(HTTPUnMarshaller.PRESERVE_LINES);
 
585
            if (o != null)
 
586
            {
 
587
               request.put(HTTPUnMarshaller.PRESERVE_LINES, o);
 
588
            }
 
589
 
 
590
            receivedInvocationRequest.set(FALSE);
 
591
            InvocationRequest invocationRequest = null;
 
592
            MessageBytes method = req.method();
 
593
            if (method.equals("GET") || method.equals("HEAD")
 
594
                  || (method.equals("OPTIONS") && req.getContentLength() <= 0))
 
595
            {
 
596
               invocationRequest = createNewInvocationRequest(request, response, null);
 
597
            } else
 
598
            {
 
599
               // must be POST or PUT
 
600
               UnMarshaller unmarshaller = getUnMarshaller();
 
601
               request.put(HTTPMetadataConstants.USE_REMOTING_CONTENT_TYPE, useRemotingContentType);
 
602
               Object obj = null;
 
603
               if (unmarshaller instanceof VersionedUnMarshaller)
 
604
                  obj = ((VersionedUnMarshaller)unmarshaller).read(request.getInputStream(), request, version);
 
605
               else
 
606
                  obj = unmarshaller.read(request.getInputStream(), request);
 
607
               if (obj instanceof InvocationRequest)
 
608
               {
 
609
                  receivedInvocationRequest.set(TRUE);
 
610
                  invocationRequest = (InvocationRequest) obj;
 
611
                  if (invocationRequest.getReturnPayload() == null)
 
612
                  {
 
613
                     // need to create a return payload map, so can be populated with metadata
 
614
                     invocationRequest.setReturnPayload(response);
 
615
                  }
 
616
                  Map requestPayloadMap = invocationRequest.getRequestPayload();
 
617
                  if (requestPayloadMap != null)
 
618
                  {
 
619
                     request.putAll(requestPayloadMap);
 
620
                  }
 
621
                  invocationRequest.setRequestPayload(request);
 
622
               } else
 
623
               {
 
624
                  invocationRequest = createNewInvocationRequest(request, response, obj);
 
625
               }
 
626
            }
 
627
 
 
628
            return invocationRequest;
 
629
         }
 
630
 
 
631
         default:
 
632
         {
 
633
            throw new IOException("Can not processes request due to incorrect version (" + version + ").  Can only process versions: " + Version.VERSION_1 + ", " + Version.VERSION_2 + ", " + Version.VERSION_2_2);
 
634
         }
 
635
      }
 
636
   }
 
637
 
 
638
   private int getVersion(RequestMap request)
 
639
   {
 
640
      int version = Version.VERSION_1; // going to default to old version
 
641
      Object versionObj = request.get(HTTPMetadataConstants.REMOTING_VERSION_HEADER);
 
642
      if (versionObj != null)
 
643
      {
 
644
         String versionString = (String) versionObj;
 
645
         try
 
646
         {
 
647
            version = Integer.parseInt(versionString);
 
648
         } catch (NumberFormatException e)
 
649
         {
 
650
            log.error("Can not processes remoting version of " + versionString + " as is not a number.");
 
651
         }
 
652
      }
 
653
      return version;
 
654
   }
 
655
 
 
656
   private void populateRequestMetadata(RequestMap metadata, Request req)
 
657
   {
 
658
      final MimeHeaders headers = req.getMimeHeaders();
 
659
      Enumeration nameEnum = null;
 
660
      
 
661
      if (SecurityUtility.skipAccessControl())
 
662
      {
 
663
         nameEnum = headers.names();
 
664
      }
 
665
      else
 
666
      {
 
667
         nameEnum = (Enumeration)AccessController.doPrivileged( new PrivilegedAction()
 
668
         {
 
669
            public Object run()
 
670
            {
 
671
               return headers.names();
 
672
            }}
 
673
         );
 
674
      }
 
675
 
 
676
      while (nameEnum.hasMoreElements())
 
677
      {
 
678
         Object nameObj = nameEnum.nextElement();
 
679
         if (nameObj instanceof String)
 
680
         {
 
681
            Object valueObj = headers.getHeader((String) nameObj);
 
682
            metadata.put(nameObj, valueObj);
 
683
         }
 
684
      }
 
685
 
 
686
      metadata.put(HTTPMetadataConstants.METHODTYPE, req.method().getString());
 
687
      metadata.put(HTTPMetadataConstants.PATH, req.requestURI().getString());
 
688
      metadata.put(HTTPMetadataConstants.QUERY, req.query().toString());
 
689
      metadata.put(HTTPMetadataConstants.HTTPVERSION, req.protocol().getString());
 
690
 
 
691
   }
 
692
 
 
693
 
 
694
   protected InvocationRequest createNewInvocationRequest(RequestMap requestMap, ResponseMap responseMap,
 
695
                                                          Object payload)
 
696
   {
 
697
      // will try to use the same session id if possible to track
 
698
      String sessionId = getSessionId(requestMap);
 
699
      String subSystem = (String) requestMap.get(HEADER_SUBSYSTEM);
 
700
 
 
701
      InvocationRequest request = null;
 
702
 
 
703
      boolean isLeaseQueury = checkForLeaseQuery(requestMap);
 
704
      if(isLeaseQueury)
 
705
      {
 
706
         addLeaseInfo(responseMap);
 
707
         request = new InvocationRequest(sessionId, subSystem, "$PING$", null, responseMap, null);
 
708
      }
 
709
      else
 
710
      {
 
711
         request = new InvocationRequest(sessionId, subSystem, payload,
 
712
                                                        requestMap, responseMap, null);
 
713
      }
 
714
      return request;
 
715
   }
 
716
 
 
717
   private boolean checkForLeaseQuery(RequestMap headers)
 
718
   {
 
719
      boolean isLeaseQuery = false;
 
720
 
 
721
         if(headers != null)
 
722
         {
 
723
            Object val = headers.get(HTTPMetadataConstants.REMOTING_LEASE_QUERY);
 
724
            if(val != null && val instanceof String)
 
725
            {
 
726
               isLeaseQuery = Boolean.valueOf((String)val).booleanValue();
 
727
            }
 
728
         }
 
729
      return isLeaseQuery;
 
730
   }
 
731
 
 
732
   /**
 
733
    * Parse additional request parameters.
 
734
    */
 
735
   protected boolean postParseRequest(org.apache.coyote.Request req,
 
736
                                      RequestMap request,
 
737
                                      org.apache.coyote.Response res,
 
738
                                      ResponseMap response)
 
739
         throws Exception
 
740
   {
 
741
 
 
742
      // URI decoding
 
743
      MessageBytes decodedURI = req.decodedURI();
 
744
      decodedURI.duplicate(req.requestURI());
 
745
 
 
746
      if(decodedURI.getType() == MessageBytes.T_BYTES)
 
747
      {
 
748
         // %xx decoding of the URL
 
749
         try
 
750
         {
 
751
            req.getURLDecoder().convert(decodedURI, false);
 
752
         }
 
753
         catch(IOException ioe)
 
754
         {
 
755
            res.setStatus(400);
 
756
            res.setMessage("Invalid URI");
 
757
            throw ioe;
 
758
         }
 
759
         // Normalization
 
760
         if(!normalize(req.decodedURI()))
 
761
         {
 
762
            res.setStatus(400);
 
763
            res.setMessage("Invalid URI");
 
764
            return false;
 
765
         }
 
766
         // Character decoding
 
767
         convertURI(decodedURI, request);
 
768
      }
 
769
      else
 
770
      {
 
771
         // The URL is chars or String, and has been sent using an in-memory
 
772
         // protocol handler, we have to assume the URL has been properly
 
773
         // decoded already
 
774
         decodedURI.toChars();
 
775
      }
 
776
 
 
777
      return true;
 
778
 
 
779
   }
 
780
 
 
781
 
 
782
   /**
 
783
    * Character conversion of the URI.
 
784
    */
 
785
   protected void convertURI(MessageBytes uri, RequestMap request)
 
786
         throws Exception
 
787
   {
 
788
 
 
789
      ByteChunk bc = uri.getByteChunk();
 
790
      CharChunk cc = uri.getCharChunk();
 
791
      cc.allocate(bc.getLength(), -1);
 
792
 
 
793
      String enc = URIEncoding;
 
794
      if(enc != null)
 
795
      {
 
796
         B2CConverter conv = request.getURIConverter();
 
797
         try
 
798
         {
 
799
            if(conv == null)
 
800
            {
 
801
               conv = new B2CConverter(enc);
 
802
               request.setURIConverter(conv);
 
803
            }
 
804
            else
 
805
            {
 
806
               conv.recycle();
 
807
            }
 
808
         }
 
809
         catch(IOException e)
 
810
         {
 
811
            // Ignore
 
812
            log.error("Invalid URI encoding; using HTTP default");
 
813
            URIEncoding = null;
 
814
         }
 
815
         if(conv != null)
 
816
         {
 
817
            try
 
818
            {
 
819
               conv.convert(bc, cc);
 
820
               uri.setChars(cc.getBuffer(), cc.getStart(),
 
821
                            cc.getLength());
 
822
               return;
 
823
            }
 
824
            catch(IOException e)
 
825
            {
 
826
               log.error("Invalid URI character encoding; trying ascii");
 
827
               cc.recycle();
 
828
            }
 
829
         }
 
830
      }
 
831
 
 
832
      // Default encoding: fast conversion
 
833
      byte[] bbuf = bc.getBuffer();
 
834
      char[] cbuf = cc.getBuffer();
 
835
      int start = bc.getStart();
 
836
      for(int i = 0; i < bc.getLength(); i++)
 
837
      {
 
838
         cbuf[i] = (char) (bbuf[i + start] & 0xff);
 
839
      }
 
840
      uri.setChars(cbuf, 0, bc.getLength());
 
841
 
 
842
   }
 
843
 
 
844
 
 
845
   /**
 
846
    * Character conversion of the a US-ASCII MessageBytes.
 
847
    */
 
848
   protected void convertMB(MessageBytes mb)
 
849
   {
 
850
 
 
851
      // This is of course only meaningful for bytes
 
852
      if(mb.getType() != MessageBytes.T_BYTES)
 
853
      {
 
854
         return;
 
855
      }
 
856
 
 
857
      ByteChunk bc = mb.getByteChunk();
 
858
      CharChunk cc = mb.getCharChunk();
 
859
      cc.allocate(bc.getLength(), -1);
 
860
 
 
861
      // Default encoding: fast conversion
 
862
      byte[] bbuf = bc.getBuffer();
 
863
      char[] cbuf = cc.getBuffer();
 
864
      int start = bc.getStart();
 
865
      for(int i = 0; i < bc.getLength(); i++)
 
866
      {
 
867
         cbuf[i] = (char) (bbuf[i + start] & 0xff);
 
868
      }
 
869
      mb.setChars(cbuf, 0, bc.getLength());
 
870
 
 
871
   }
 
872
 
 
873
 
 
874
   /**
 
875
    * Normalize URI.
 
876
    * <p/>
 
877
    * This method normalizes "\", "//", "/./" and "/../". This method will
 
878
    * return false when trying to go above the root, or if the URI contains
 
879
    * a null byte.
 
880
    *
 
881
    * @param uriMB URI to be normalized
 
882
    */
 
883
   public static boolean normalize(MessageBytes uriMB)
 
884
   {
 
885
 
 
886
      ByteChunk uriBC = uriMB.getByteChunk();
 
887
      byte[] b = uriBC.getBytes();
 
888
      int start = uriBC.getStart();
 
889
      int end = uriBC.getEnd();
 
890
      
 
891
      // Expect request URI to be at least one character.
 
892
      if (start - end == 0)
 
893
      {
 
894
         return false;
 
895
      }
 
896
      
 
897
      // URL * is acceptable
 
898
      if((end - start == 1) && b[start] == (byte) '*')
 
899
      {
 
900
         return true;
 
901
      }
 
902
 
 
903
      int pos = 0;
 
904
      int index = 0;
 
905
 
 
906
      // Replace '\' with '/'
 
907
      // Check for null byte
 
908
      for(pos = start; pos < end; pos++)
 
909
      {
 
910
         if(b[pos] == (byte) '\\')
 
911
         {
 
912
            b[pos] = (byte) '/';
 
913
         }
 
914
         if(b[pos] == (byte) 0)
 
915
         {
 
916
            return false;
 
917
         }
 
918
      }
 
919
 
 
920
      // The URL must start with '/'
 
921
      if(b[start] != (byte) '/')
 
922
      {
 
923
         return false;
 
924
      }
 
925
 
 
926
      // Replace "//" with "/"
 
927
      for(pos = start; pos < (end - 1); pos++)
 
928
      {
 
929
         if(b[pos] == (byte) '/')
 
930
         {
 
931
            while((pos + 1 < end) && (b[pos + 1] == (byte) '/'))
 
932
            {
 
933
               copyBytes(b, pos, pos + 1, end - pos - 1);
 
934
               end--;
 
935
            }
 
936
         }
 
937
      }
 
938
 
 
939
      // If the URI ends with "/." or "/..", then we append an extra "/"
 
940
      // Note: It is possible to extend the URI by 1 without any side effect
 
941
      // as the next character is a non-significant WS.
 
942
      if(((end - start) >= 2) && (b[end - 1] == (byte) '.'))
 
943
      {
 
944
         if((b[end - 2] == (byte) '/')
 
945
            || ((b[end - 2] == (byte) '.')
 
946
                && (b[end - 3] == (byte) '/')))
 
947
         {
 
948
            b[end] = (byte) '/';
 
949
            end++;
 
950
         }
 
951
      }
 
952
 
 
953
      uriBC.setEnd(end);
 
954
 
 
955
      index = 0;
 
956
 
 
957
      // Resolve occurrences of "/./" in the normalized path
 
958
      while(true)
 
959
      {
 
960
         index = uriBC.indexOf("/./", 0, 3, index);
 
961
         if(index < 0)
 
962
         {
 
963
            break;
 
964
         }
 
965
         copyBytes(b, start + index, start + index + 2,
 
966
                   end - start - index - 2);
 
967
         end = end - 2;
 
968
         uriBC.setEnd(end);
 
969
      }
 
970
 
 
971
      index = 0;
 
972
 
 
973
      // Resolve occurrences of "/../" in the normalized path
 
974
      while(true)
 
975
      {
 
976
         index = uriBC.indexOf("/../", 0, 4, index);
 
977
         if(index < 0)
 
978
         {
 
979
            break;
 
980
         }
 
981
         // Prevent from going outside our context
 
982
         if(index == 0)
 
983
         {
 
984
            return false;
 
985
         }
 
986
         int index2 = -1;
 
987
         for(pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --)
 
988
         {
 
989
            if(b[pos] == (byte) '/')
 
990
            {
 
991
               index2 = pos;
 
992
            }
 
993
         }
 
994
         copyBytes(b, start + index2, start + index + 3,
 
995
                   end - start - index - 3);
 
996
         end = end + index2 - index - 3;
 
997
         uriBC.setEnd(end);
 
998
         index = index2;
 
999
      }
 
1000
 
 
1001
      uriBC.setBytes(b, start, end);
 
1002
 
 
1003
      return true;
 
1004
 
 
1005
   }
 
1006
 
 
1007
 
 
1008
   /**
 
1009
    * Copy an array of bytes to a different position. Used during
 
1010
    * normalization.
 
1011
    */
 
1012
   protected static void copyBytes(byte[] b, int dest, int src, int len)
 
1013
   {
 
1014
      for(int pos = 0; pos < len; pos++)
 
1015
      {
 
1016
         b[pos + dest] = b[pos + src];
 
1017
      }
 
1018
   }
 
1019
 
 
1020
 
 
1021
   public void stop()
 
1022
   {
 
1023
      if(running)
 
1024
      {
 
1025
         running = false;
 
1026
 
 
1027
         if (protocolHandlers != null)
 
1028
         {
 
1029
            Iterator it = protocolHandlers.iterator();
 
1030
            while (it.hasNext())
 
1031
            {
 
1032
               try
 
1033
               {
 
1034
                  ProtocolHandler protocolHandler = (ProtocolHandler) it.next();
 
1035
                  protocolHandler.destroy();
 
1036
               }
 
1037
               catch(Exception e)
 
1038
               {
 
1039
                  log.error("Stop error", e);
 
1040
               }
 
1041
            }
 
1042
         }
 
1043
      }
 
1044
      super.stop();
 
1045
 
 
1046
      log.debug("CoyoteInvoker stopped.");
 
1047
   }
 
1048
 
 
1049
   /**
 
1050
    * Find a method with the right name If found, call the method ( if param is
 
1051
    * int or boolean we'll convert value to the right type before) - that means
 
1052
    * you can have setDebug(1).
 
1053
    */
 
1054
   public static boolean setProperty(final Object o, String name, final String value)
 
1055
   {
 
1056
      String setter = "set" + capitalize(name);
 
1057
 
 
1058
      try
 
1059
      {
 
1060
         Method[] methods = null;
 
1061
         
 
1062
         if (SecurityUtility.skipAccessControl())
 
1063
         {
 
1064
            methods = o.getClass().getMethods();
 
1065
         }
 
1066
         else
 
1067
         {
 
1068
            methods = (Method[]) AccessController.doPrivileged( new PrivilegedAction()
 
1069
            {
 
1070
               public Object run()
 
1071
               {
 
1072
                  return o.getClass().getMethods();
 
1073
               }
 
1074
            });
 
1075
         }
 
1076
         
 
1077
         Method setPropertyMethod = null;
 
1078
 
 
1079
         // First, the ideal case - a setFoo( String ) method
 
1080
         for(int i = 0; i < methods.length; i++)
 
1081
         {
 
1082
            Class paramT[] = methods[i].getParameterTypes();
 
1083
            if(setter.equals(methods[i].getName()) && paramT.length == 1
 
1084
               && "java.lang.String".equals(paramT[0].getName()))
 
1085
            {
 
1086
 
 
1087
               methods[i].invoke(o, new Object[]{value});
 
1088
               return true;
 
1089
            }
 
1090
         }
 
1091
 
 
1092
         // Try a setFoo ( int ) or ( boolean )
 
1093
         for(int i = 0; i < methods.length; i++)
 
1094
         {
 
1095
            boolean ok = true;
 
1096
            if(setter.equals(methods[i].getName())
 
1097
               && methods[i].getParameterTypes().length == 1)
 
1098
            {
 
1099
 
 
1100
               // match - find the type and invoke it
 
1101
               Class paramType = methods[i].getParameterTypes()[0];
 
1102
               Object params[] = new Object[1];
 
1103
 
 
1104
               // Try a setFoo ( int )
 
1105
               if("java.lang.Integer".equals(paramType.getName())
 
1106
                  || "int".equals(paramType.getName()))
 
1107
               {
 
1108
                  try
 
1109
                  {
 
1110
                     params[0] = new Integer(value);
 
1111
                  }
 
1112
                  catch(NumberFormatException ex)
 
1113
                  {
 
1114
                     ok = false;
 
1115
                  }
 
1116
                  // Try a setFoo ( long )
 
1117
               }
 
1118
               else if("java.lang.Long".equals(paramType.getName())
 
1119
                       || "long".equals(paramType.getName()))
 
1120
               {
 
1121
                  try
 
1122
                  {
 
1123
                     params[0] = new Long(value);
 
1124
                  }
 
1125
                  catch(NumberFormatException ex)
 
1126
                  {
 
1127
                     ok = false;
 
1128
                  }
 
1129
 
 
1130
                  // Try a setFoo ( boolean )
 
1131
               }
 
1132
               else if("java.lang.Boolean".equals(paramType.getName())
 
1133
                       || "boolean".equals(paramType.getName()))
 
1134
               {
 
1135
                  params[0] = new Boolean(value);
 
1136
 
 
1137
                  // Try a setFoo ( InetAddress )
 
1138
               }
 
1139
               else if("java.net.InetAddress".equals(paramType.getName()))
 
1140
               {
 
1141
                  try
 
1142
                  {
 
1143
                     params[0] = getAddressByName(value);
 
1144
                  }
 
1145
                  catch(UnknownHostException exc)
 
1146
                  {
 
1147
                     ok = false;
 
1148
                  }
 
1149
 
 
1150
                  // Unknown type
 
1151
               }
 
1152
 
 
1153
               if(ok)
 
1154
               {
 
1155
                  methods[i].invoke(o, params);
 
1156
                  return true;
 
1157
               }
 
1158
            }
 
1159
 
 
1160
            // save "setProperty" for later
 
1161
            if("setProperty".equals(methods[i].getName()))
 
1162
            {
 
1163
               setPropertyMethod = methods[i];
 
1164
            }
 
1165
         }
 
1166
 
 
1167
         // Ok, no setXXX found, try a setProperty("name", "value")
 
1168
         if(setPropertyMethod != null)
 
1169
         {
 
1170
            Object params[] = new Object[2];
 
1171
            params[0] = name;
 
1172
            params[1] = value;
 
1173
            setPropertyMethod.invoke(o, params);
 
1174
            return true;
 
1175
         }
 
1176
 
 
1177
      }
 
1178
      catch(Exception e)
 
1179
      {
 
1180
         return false;
 
1181
      }
 
1182
      return false;
 
1183
   }
 
1184
 
 
1185
   /**
 
1186
    * Reverse of Introspector.decapitalize
 
1187
    */
 
1188
   public static String capitalize(String name)
 
1189
   {
 
1190
      if(name == null || name.length() == 0)
 
1191
      {
 
1192
         return name;
 
1193
      }
 
1194
      char chars[] = name.toCharArray();
 
1195
      chars[0] = Character.toUpperCase(chars[0]);
 
1196
      return new String(chars);
 
1197
   }
 
1198
 
 
1199
   
 
1200
   /**
 
1201
    * Necessary for implementation of org.apache.coyote.Adapter interface.
 
1202
    * Body is vacuous because event() is only used in comet mode.
 
1203
    */
 
1204
   public boolean event(Request arg0, Response arg1, SocketStatus arg2) throws Exception
 
1205
   {
 
1206
      return true;
 
1207
   }
 
1208
   
 
1209
   static private boolean isInvalidContentType(String contentType)
 
1210
   {
 
1211
      return contentType.indexOf('\n') + contentType.indexOf('\r') > -2;
 
1212
   }
 
1213
   
 
1214
   static private Object forName(final String className) throws ClassNotFoundException
 
1215
   {
 
1216
      if (SecurityUtility.skipAccessControl())
 
1217
      {
 
1218
         return Class.forName(className);
 
1219
      }
 
1220
      
 
1221
      try
 
1222
      {
 
1223
         return  AccessController.doPrivileged( new PrivilegedExceptionAction()
 
1224
         {
 
1225
            public Object run() throws Exception
 
1226
            {
 
1227
               return Class.forName(className);
 
1228
            }
 
1229
         });
 
1230
      }
 
1231
      catch (PrivilegedActionException e)
 
1232
      {
 
1233
         throw (ClassNotFoundException) e.getCause();
 
1234
      }
 
1235
   }
 
1236
   
 
1237
   static private InetAddress getAddressByName(final String host) throws UnknownHostException
 
1238
   {
 
1239
      if (SecurityUtility.skipAccessControl())
 
1240
      {
 
1241
         return InetAddress.getByName(host);
 
1242
      }
 
1243
      
 
1244
      try
 
1245
      {
 
1246
         return (InetAddress)AccessController.doPrivileged( new PrivilegedExceptionAction()
 
1247
         {
 
1248
            public Object run() throws IOException
 
1249
            {
 
1250
               return InetAddress.getByName(host);
 
1251
            }
 
1252
         });
 
1253
      }
 
1254
      catch (PrivilegedActionException e)
 
1255
      {
 
1256
         throw (UnknownHostException) e.getCause();
 
1257
      }
 
1258
   }
 
1259
}