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

Quick Search    Search Deep

Source code: org/apache/axis/MessageContext.java


1   /*
2    * Copyright 2001-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.axis ;
18  
19  import org.apache.axis.attachments.Attachments;
20  import org.apache.axis.client.AxisClient;
21  import org.apache.axis.components.logger.LogFactory;
22  import org.apache.axis.description.OperationDesc;
23  import org.apache.axis.description.ServiceDesc;
24  import org.apache.axis.encoding.TypeMapping;
25  import org.apache.axis.encoding.TypeMappingRegistry;
26  import org.apache.axis.constants.Style;
27  import org.apache.axis.constants.Use;
28  import org.apache.axis.handlers.soap.SOAPService;
29  import org.apache.axis.schema.SchemaVersion;
30  import org.apache.axis.session.Session;
31  import org.apache.axis.soap.SOAPConstants;
32  import org.apache.axis.utils.JavaUtils;
33  import org.apache.axis.utils.LockableHashtable;
34  import org.apache.axis.utils.Messages;
35  import org.apache.commons.logging.Log;
36  
37  import javax.xml.namespace.QName;
38  import javax.xml.rpc.Call;
39  import javax.xml.rpc.handler.soap.SOAPMessageContext;
40  import java.io.File;
41  import java.util.ArrayList;
42  import java.util.Hashtable;
43  
44  // fixme: fields are declared throughout this class, some at the top, and some
45  //  near to where they are used. We should move all field declarations into a
46  //  single block - it makes it easier to see what is decalred in this class and
47  //  what is inherited. It also makes it easier to find them.
48  /**
49   * A MessageContext is the Axis implementation of the javax
50   * SOAPMessageContext class, and is core to message processing
51   * in handlers and other parts of the system.
52   *
53   * This class also contains constants for accessing some
54   * well-known properties. Using a hierarchical namespace is
55   * strongly suggested in order to lower the chance for
56   * conflicts.
57   *
58   * (These constants should be viewed as an explicit list of well
59   *  known and widely used context keys, there's nothing wrong
60   *  with directly using the key strings. This is the reason for
61   *  the hierarchical constant namespace.
62   *
63   *  Actually I think we might just list the keys in the docs and
64   *  provide no such constants since they create yet another
65   *  namespace, but we'd have no compile-time checks then.
66   *
67   *  Whaddya think? - todo by Jacek)
68   *
69   *
70   * @author Doug Davis (dug@us.ibm.com)
71   * @author Jacek Kopecky (jacek@idoox.com)
72   */
73  public class MessageContext implements SOAPMessageContext {
74      /** The <code>Log</code> used for logging all messages. */
75      protected static Log log =
76              LogFactory.getLog(MessageContext.class.getName());
77  
78      /**
79       * The request message.  If we're on the client, this is the outgoing
80       * message heading to the server.  If we're on the server, this is the
81       * incoming message we've received from the client.
82       */
83      private Message      requestMessage;
84  
85      /**
86       * The response message.  If we're on the server, this is the outgoing
87       * message heading back to the client.  If we're on the client, this is the
88       * incoming message we've received from the server.
89       */
90      private Message      responseMessage;
91  
92      /**
93       * That unique key/name that the next router/dispatch handler should use
94       * to determine what to do next.
95       */
96      private String       targetService;
97  
98      /**
99       * The name of the Transport which this message was received on (or is
100      * headed to, for the client).
101      */
102     private String       transportName;
103 
104     /**
105      * The default <code>ClassLoader</code> that this service should use.
106      */
107     private ClassLoader  classLoader;
108 
109     /**
110      * The AxisEngine which this context is involved with.
111      */
112     private AxisEngine   axisEngine;
113 
114     /**
115      * A Session associated with this request.
116      */
117     private Session      session;
118 
119     /**
120      * Should we track session state, or not?
121      * default is not.
122      * Could potentially refactor this so that
123      * maintainSession iff session != null...
124      */
125     private boolean      maintainSession = false;
126 
127     // fixme: ambiguity here due to lac of docs - havePassedPivot vs
128     //  request/response, vs sending/processing & recieving/responding
129     //  I may have just missed the key bit of text
130     /**
131      * Are we doing request stuff, or response stuff? True if processing
132      * response (I think).
133      */
134     private boolean      havePassedPivot = false;
135 
136     /**
137      * Maximum amount of time to wait on a request, in milliseconds.
138      */
139     private int          timeout = Constants.DEFAULT_MESSAGE_TIMEOUT;
140 
141     /**
142      * An indication of whether we require "high fidelity" recording of
143      * deserialized messages for this interaction.  Defaults to true for
144      * now, and can be set to false, usually at service-dispatch time.
145      */
146     private boolean      highFidelity = true;
147 
148     /**
149      * Storage for an arbitrary bag of properties associated with this
150      * MessageContext.
151      */
152     private LockableHashtable bag = new LockableHashtable();
153 
154     /*
155      * These variables are logically part of the bag, but are separated
156      * because they are used often and the Hashtable is more expensive.
157      *
158      * fixme: this may be fixed by moving to a plain Map impl like HashMap.
159      *  Alternatively, we could hide all this magic behind a custom Map impl -
160      *  is synchronization on the map needed? these properties aren't
161      *  synchronized so I'm guessing not.
162      */
163     private String  username       = null;
164     private String  password       = null;
165     private String  encodingStyle  = Use.ENCODED.getEncoding();
166     private boolean useSOAPAction  = false;
167     private String  SOAPActionURI  = null;
168 
169     /**
170      * SOAP Actor roles.
171      */
172     private String[] roles;
173 
174     /** Our SOAP namespaces and such. */
175     private SOAPConstants soapConstants = Constants.DEFAULT_SOAP_VERSION;
176 
177     /** Schema version information - defaults to 2001. */
178     private SchemaVersion schemaVersion = SchemaVersion.SCHEMA_2001;
179 
180     /** Our current operation. */
181     private OperationDesc currentOperation = null;
182 
183     /**
184      * The current operation.
185      *
186      * @return the current operation; may be <code>null</code>
187      */
188     public  OperationDesc getOperation()
189     {
190         return currentOperation;
191     }
192 
193     /**
194      * Set the current operation.
195      *
196      * @param operation  the <code>Operation</code> this context is executing
197      */
198     public void setOperation(OperationDesc operation)
199     {
200         currentOperation = operation;
201     }
202 
203     /**
204      * Returns a list of operation descriptors that could may
205      * possibly match a body containing an element of the given QName.
206      * For non-DOCUMENT, the list of operation descriptors that match
207      * the name is returned.  For DOCUMENT, all the operations that have
208      * qname as a parameter are returned
209      *
210      * @param qname of the first element in the body
211      * @return list of operation descriptions
212      * @throws AxisFault if the operation names could not be looked up
213      */
214     public OperationDesc [] getPossibleOperationsByQName(QName qname) throws AxisFault
215     {
216         if (currentOperation != null) {
217             return new OperationDesc [] { currentOperation };
218         }
219 
220         OperationDesc [] possibleOperations = null;
221 
222         if (serviceHandler == null) {
223             try {
224                 if (log.isDebugEnabled()) {
225                     log.debug(Messages.getMessage("dispatching00",
226                                                    qname.getNamespaceURI()));
227                 }
228 
229                 // Try looking this QName up in our mapping table...
230                 setService(axisEngine.getConfig().
231                            getServiceByNamespaceURI(qname.getNamespaceURI()));
232             } catch (ConfigurationException e) {
233                 // Didn't find one...
234             }
235 
236         }
237 
238         if (serviceHandler != null) {
239             ServiceDesc desc = serviceHandler.getInitializedServiceDesc(this);
240 
241             if (desc != null) {
242                 if (desc.getStyle() != Style.DOCUMENT) {
243                     possibleOperations = desc.getOperationsByQName(qname);
244                 } else {
245                     // DOCUMENT Style
246                     // Get all of the operations that have qname as
247                     // a possible parameter QName
248                     ArrayList allOperations = desc.getOperations();
249                     ArrayList foundOperations = new ArrayList();
250                     for (int i=0; i < allOperations.size(); i++ ) {
251                         OperationDesc tryOp =
252                             (OperationDesc) allOperations.get(i);
253                         if (tryOp.getParamByQName(qname) != null) {
254                             foundOperations.add(tryOp);
255                         }
256                     }
257                     if (foundOperations.size() > 0) {
258                         possibleOperations = (OperationDesc[])
259                             JavaUtils.convert(foundOperations,
260                                               OperationDesc[].class);
261                     }
262                 }
263             }
264         }
265         return possibleOperations;
266     }
267 
268     /**
269      * get the first possible operation that could match a
270      * body containing an element of the given QName. Sets the currentOperation
271      * field in the process; if that field is already set then its value
272      * is returned instead
273      * @param qname name of the message body
274      * @return an operation or null
275      * @throws AxisFault
276      */
277     public OperationDesc getOperationByQName(QName qname) throws AxisFault
278     {
279         if (currentOperation == null) {
280             OperationDesc [] possibleOperations = getPossibleOperationsByQName(qname);
281             if (possibleOperations != null && possibleOperations.length > 0) {
282                 currentOperation = possibleOperations[0];
283             }
284         }
285 
286         return currentOperation;
287     }
288 
289     /**
290      * Get the active message context.
291      *
292      * @return the current active message context
293      */
294     public static MessageContext getCurrentContext() {
295        return AxisEngine.getCurrentMessageContext();
296     }
297 
298     /**
299      * Temporary directory to store attachments.
300      */
301     protected static String systemTempDir= null;
302     /**
303      * set the temp dir
304      * TODO: move this piece of code out of this class and into a utilities
305      * class.
306      */
307     static {
308         try {
309             //get the temp dir from the engine
310             systemTempDir=AxisProperties.getProperty(AxisEngine.ENV_ATTACHMENT_DIR);
311         } catch(Throwable t) {
312             systemTempDir= null;
313         }
314 
315         if(systemTempDir== null) {
316             try {
317                 //or create and delete a file in the temp dir to make
318                 //sure we have write access to it.
319                 File tf= File.createTempFile("Axis", ".tmp");
320                 File dir= tf.getParentFile();
321                 if (tf.exists()) {
322                     tf.delete();
323                 }
324                 if (dir != null) {
325                   systemTempDir= dir.getCanonicalPath();
326                 }
327             } catch(Throwable t) {
328                 log.debug("Unable to find a temp dir with write access");
329                 systemTempDir= null;
330             }
331         }
332     }
333 
334     /**
335      * Create a message context.
336      * @param engine the controlling axis engine. Null is actually accepted here,
337      * though passing a null engine in is strongly discouraged as many of the methods
338      * assume that it is in fact defined.
339      */
340     public MessageContext(AxisEngine engine) {
341         this.axisEngine = engine;
342 
343         if(null != engine){
344             java.util.Hashtable opts= engine.getOptions();
345             String attachmentsdir= null;
346             if(null!=opts) {
347                 attachmentsdir= (String) opts.get(AxisEngine.PROP_ATTACHMENT_DIR);
348             }
349             if(null == attachmentsdir) {
350                 attachmentsdir= systemTempDir;
351             }
352             if(attachmentsdir != null){
353                 setProperty(ATTACHMENTS_DIR, attachmentsdir);
354             }
355 
356             // If SOAP 1.2 has been specified as the default for the engine,
357             // switch the constants over.
358             String defaultSOAPVersion = (String)engine.getOption(
359                                                  AxisEngine.PROP_SOAP_VERSION);
360             if (defaultSOAPVersion != null && "1.2".equals(defaultSOAPVersion)) {
361                 setSOAPConstants(SOAPConstants.SOAP12_CONSTANTS);
362             }
363 
364             String singleSOAPVersion = (String)engine.getOption(
365                                         AxisEngine.PROP_SOAP_ALLOWED_VERSION);
366             if (singleSOAPVersion != null) {
367                 if ("1.2".equals(singleSOAPVersion)) {
368                     setProperty(Constants.MC_SINGLE_SOAP_VERSION,
369                                 SOAPConstants.SOAP12_CONSTANTS);
370                 } else if ("1.1".equals(singleSOAPVersion)) {
371                     setProperty(Constants.MC_SINGLE_SOAP_VERSION,
372                                 SOAPConstants.SOAP11_CONSTANTS);
373                 }
374             }
375         }
376     }
377 
378     /**
379      * during finalization, the dispose() method is called.
380      * @see #dispose()
381      */
382     protected void finalize() {
383         dispose();
384     }
385 
386     /**
387      * Mappings of QNames to serializers/deserializers (and therfore
388      * to Java types).
389      */
390     private TypeMappingRegistry mappingRegistry = null;
391 
392     /**
393      * Replace the engine's type mapping registry with a local one. This will
394      * have no effect on any type mappings obtained before this call.
395      *
396      * @param reg  the new <code>TypeMappingRegistry</code>
397      */
398     public void setTypeMappingRegistry(TypeMappingRegistry reg) {
399         mappingRegistry = reg;
400     }
401 
402     /**
403      * Get the currently in-scope type mapping registry.
404      *
405      * By default, will return a reference to the AxisEngine's TMR until
406      * someone sets our local one (usually as a result of setting the
407      * serviceHandler).
408      *
409      * @return the type mapping registry to use for this request.
410      */
411     public TypeMappingRegistry getTypeMappingRegistry() {
412         if (mappingRegistry == null) {
413             return axisEngine.getTypeMappingRegistry();
414         }
415 
416         return mappingRegistry;
417     }
418 
419     /**
420      * Return the type mapping currently in scope for our encoding style.
421      *
422      * @return the type mapping
423      */
424     public TypeMapping getTypeMapping()
425     {
426         return (TypeMapping)getTypeMappingRegistry().
427                 getTypeMapping(encodingStyle);
428     }
429 
430     /**
431      * The name of the transport for this context.
432      *
433      * @return the transport name
434      */
435     public String getTransportName()
436     {
437         return transportName;
438     }
439 
440     // fixme: the transport names should be a type-safe e-num, or the range
441     //  of legal values should be specified in the documentation and validated
442     //  in the method (raising IllegalArgumentException)
443     /**
444      * Set the transport name for this context.
445      *
446      * @param transportName the name of the transport
447      */
448     public void setTransportName(String transportName)
449     {
450         this.transportName = transportName;
451     }
452 
453     /**
454      * Get the <code>SOAPConstants</code> used by this message context.
455      *
456      * @return the soap constants
457      */
458     public SOAPConstants getSOAPConstants() {
459         return soapConstants;
460     }
461 
462     /**
463      * Set the <code>SOAPConstants</code> used by this message context.
464      * This may also affect the encoding style.
465      *
466      * @param soapConstants  the new soap constants to use
467      */
468     public void setSOAPConstants(SOAPConstants soapConstants) {
469         // when changing SOAP versions, remember to keep the encodingURI
470         // in synch.
471         if (this.soapConstants.getEncodingURI().equals(encodingStyle)) {
472             encodingStyle = soapConstants.getEncodingURI();
473         }
474 
475         this.soapConstants = soapConstants;
476     }
477 
478     /**
479      * Get the XML schema version information.
480      *
481      * @return the <code>SchemaVersion</code> in use
482      */
483     public SchemaVersion getSchemaVersion() {
484         return schemaVersion;
485     }
486 
487     /**
488      * Set the XML schema version this message context will use.
489      *
490      * @param schemaVersion  the new <code>SchemaVersion</code>
491      */
492     public void setSchemaVersion(SchemaVersion schemaVersion) {
493         this.schemaVersion = schemaVersion;
494     }
495 
496     /**
497      * Get the current session.
498      *
499      * @return the <code>Session</code> this message context is within
500      */
501     public Session getSession()
502     {
503         return session;
504     }
505 
506     /**
507      * Set the current session.
508      *
509      * @param session  the new <code>Session</code>
510      */
511     public void setSession(Session session)
512     {
513         this.session = session;
514     }
515 
516     /**
517      * Indicates if the opration is encoded.
518      *
519      * @return <code>true</code> if it is encoded, <code>false</code> otherwise
520      */
521     public boolean isEncoded() {
522         return (getOperationUse() == Use.ENCODED);
523         //return soapConstants.getEncodingURI().equals(encodingStyle);
524     }
525 
526     /**
527      * Set whether we are maintaining session state.
528      *
529      * @param yesno flag to set to <code>true</code> to maintain sessions
530      */
531     public void setMaintainSession (boolean yesno) {
532         maintainSession = yesno;
533     }
534 
535     /**
536      * Discover if we are maintaining session state.
537      *
538      * @return <code>true</code> if we are maintaining state, <code>false</code>
539      *              otherwise
540      */
541     public boolean getMaintainSession () {
542         return maintainSession;
543     }
544 
545     /**
546      * Get the request message.
547      *
548      * @return the request message (may be null).
549      */
550     public Message getRequestMessage() {
551         return requestMessage ;
552     }
553 
554     /**
555      * Set the request message, and make sure that message is associated
556      * with this MessageContext.
557      *
558      * @param reqMsg the new request Message.
559      */
560     public void setRequestMessage(Message reqMsg) {
561         requestMessage = reqMsg ;
562         if (requestMessage != null) {
563             requestMessage.setMessageContext(this);
564         }
565     }
566 
567     /**
568      * Get the response message.
569      *
570      * @return the response message (may be null).
571      */
572     public Message getResponseMessage() { return responseMessage ; }
573 
574     /**
575      * Set the response message, and make sure that message is associated
576      * with this MessageContext.
577      *
578      * @param respMsg the new response Message.
579      */
580     public void setResponseMessage(Message respMsg) {
581         responseMessage = respMsg;
582         if (responseMessage != null) {
583             responseMessage.setMessageContext(this);
584 
585             //if we have received attachments of a particular type
586             // than that should be the default type to send.
587             Message reqMsg = getRequestMessage();
588             if (null != reqMsg) {
589                 Attachments reqAttch = reqMsg.getAttachmentsImpl();
590                 Attachments respAttch = respMsg.getAttachmentsImpl();
591                 if (null != reqAttch && null != respAttch) {
592                     if (respAttch.getSendType() == Attachments.SEND_TYPE_NOTSET)
593                         //only if not explicity set.
594                         respAttch.setSendType(reqAttch.getSendType());
595                 }
596             }
597         }
598     }
599 
600     /**
601      * Return the current (i.e. request before the pivot, response after)
602      * message.
603      *
604      * @return the current <code>Message</code>
605      */
606     public Message getCurrentMessage()
607     {
608         return (havePassedPivot ? responseMessage : requestMessage);
609     }
610 
611     /**
612      *  Gets the SOAPMessage from this message context.
613      *
614      *  @return the <code>SOAPMessage</code>, <code>null</code> if no request
615      *          <code>SOAPMessage</code> is present in this
616      *          <code>SOAPMessageContext</code>
617      */
618     public javax.xml.soap.SOAPMessage getMessage() {
619         return getCurrentMessage();
620     }
621 
622     /**
623      * Set the current message. This will set the request before the pivot,
624      * and the response afterwards, as guaged by the passedPivod property.
625      *
626      * @param curMsg  the <code>Message</code> to assign
627      */
628     public void setCurrentMessage(Message curMsg)
629     {
630         curMsg.setMessageContext(this);
631 
632         if (havePassedPivot) {
633             responseMessage = curMsg;
634         } else {
635             requestMessage = curMsg;
636         }
637     }
638 
639     /**
640      * Sets the SOAPMessage for this message context.
641      * This is equivalent to casting <code>message</code> to
642      * <code>Message</code> and then passing it on to
643      * <code>setCurrentMessage()</code>.
644      *
645      * @param message  the <code>SOAPMessage</code> this context is for
646      */
647     public void setMessage(javax.xml.soap.SOAPMessage message) {
648         setCurrentMessage((Message)message);
649     }
650 
651     /**
652      * Determine when we've passed the pivot.
653      *
654      * @return <code>true</code> if we have, <code>false</code> otherwise
655      */
656     public boolean getPastPivot()
657     {
658         return havePassedPivot;
659     }
660 
661     // fixme: is there any legitimate case where we could pass the pivot and
662     //  then go back again? Is there documentation about the life-cycle of a
663     //  MessageContext, and in particular the re-use of instances that would be
664     //  relevant?
665     /**
666      * Indicate when we've passed the pivot.
667      *
668      * @param pastPivot  true if we are past the pivot point, false otherwise
669      */
670     public void setPastPivot(boolean pastPivot)
671     {
672         havePassedPivot = pastPivot;
673     }
674 
675     /**
676      * Set timeout in our MessageContext.
677      *
678      * @param value the maximum amount of time, in milliseconds
679      */
680     public void setTimeout (int value) {
681         timeout = value;
682     }
683 
684     /**
685      * Get timeout from our MessageContext.
686      *
687      * @return value the maximum amount of time, in milliseconds
688      */
689     public int getTimeout () {
690         return timeout;
691     }
692 
693     /**
694      * Get the classloader, implicitly binding to the thread context
695      * classloader if an override has not been supplied.
696      *
697      * @return the class loader
698      */
699     public ClassLoader getClassLoader() {
700         if ( classLoader == null ) {
701             classLoader = Thread.currentThread().getContextClassLoader();
702         }
703         return( classLoader );
704     }
705 
706     /**
707      * Set a new classloader. Setting to null will result in getClassLoader()
708      * binding back to the thread context class loader.
709      *
710      * @param cl    the new <code>ClassLoader</code> or <code>null</code>
711      */
712     public void setClassLoader(ClassLoader cl ) {
713         classLoader = cl ;
714     }
715 
716     /**
717      * Get the name of the targed service for this message.
718      *
719      * @return the target service
720      */
721     public String getTargetService() {
722         return targetService;
723     }
724 
725     /**
726      * Get the axis engine. This will be <code>null</code> if the message was
727      * created outside an engine
728      *
729      * @return the current axis engine
730      */
731     public AxisEngine getAxisEngine()
732     {
733         return axisEngine;
734     }
735 
736     /**
737      * Set the target service for this message.
738      * <p>
739      * This looks up the named service in the registry, and has
740      * the side effect of setting our TypeMappingRegistry to the
741      * service's.
742      *
743      * @param tServ the name of the target service
744      * @throws AxisFault  if anything goes wrong in resolving or setting the
745      *              service
746      */
747     public void setTargetService(String tServ) throws AxisFault {
748         log.debug("MessageContext: setTargetService(" + tServ+")");
749 
750         if (tServ == null) {
751             setService(null);
752         }
753         else {
754             try {
755                 setService(getAxisEngine().getService(tServ));
756             } catch (AxisFault fault) {
757                 // If we're on the client, don't throw this fault...
758                 if (!isClient()) {
759                     throw fault;
760                 }
761             }
762         }
763         targetService = tServ;
764     }
765 
766     /** ServiceHandler is the handler that is the "service".  This handler
767      * can (and probably will actually be a chain that contains the
768      * service specific request/response/pivot point handlers
769      */
770     private SOAPService serviceHandler ;
771 
772     /**
773      * Get the <code>SOAPService</code> used to handle services in this
774      * context.
775      *
776      * @return the service handler
777      */
778     public SOAPService getService() {
779         return  serviceHandler;
780     }
781 
782     /**
783      * Set the <code>SOAPService</code> used to handle services in this
784      * context. This method configures a wide range of
785      * <code>MessageContext</code> properties to suit the handler.
786      *
787      * @param sh the new service handler
788      * @throws AxisFault if the service could not be set
789      */
790     public void setService(SOAPService sh) throws AxisFault
791     {
792         log.debug("MessageContext: setServiceHandler("+sh+")");
793         serviceHandler = sh;
794         if (sh != null) {
795             if(!sh.isRunning()) {
796                 throw new AxisFault(Messages.getMessage("disabled00"));
797             }
798             targetService = sh.getName();
799             SOAPService service = sh;
800             TypeMappingRegistry tmr = service.getTypeMappingRegistry();
801             setTypeMappingRegistry(tmr);
802 
803             // styles are not "soap version aware" so compensate...
804             setEncodingStyle(service.getUse().getEncoding());
805 
806             // This MessageContext should now defer properties it can't find
807             // to the Service's options.
808             bag.setParent(sh.getOptions());
809 
810             // Note that we need (or don't need) high-fidelity SAX recording
811             // of deserialized messages according to the setting on the
812             // new service.
813             highFidelity = service.needsHighFidelityRecording();
814 
815             service.getInitializedServiceDesc(this);
816         }
817     }
818 
819     /**
820      * Let us know whether this is the client or the server.
821      *
822      * @return true if we are a client
823      */
824     public boolean isClient()
825     {
826         return (axisEngine instanceof AxisClient);
827     }
828 
829     // fixme: public final statics tend to go in a block at the top of the
830     //  class deffinition, not marooned in the middle
831     // fixme: chose public static final /or/ public final static
832     /** Contains an instance of Handler, which is the
833      *  ServiceContext and the entrypoint of this service.
834      *
835      *  (if it has been so configured - will our deployment
836      *   tool do this by default?  - todo by Jacek)
837      */
838     public static final String ENGINE_HANDLER      = "engine.handler";
839 
840     /** This String is the URL that the message came to.
841      */
842     public static final String TRANS_URL           = "transport.url";
843 
844     /** Has a quit been requested? Hackish... but useful... -- RobJ */
845     public static final String QUIT_REQUESTED = "quit.requested";
846 
847     /** Place to store an AuthenticatedUser. */
848     public static final String AUTHUSER            = "authenticatedUser";
849 
850     /** If on the client - this is the Call object. */
851     public static final String CALL                = "call_object" ;
852 
853     /** Are we doing Msg vs RPC? - For Java Binding. */
854     public static final String IS_MSG              = "isMsg" ;
855 
856     /** The directory where in coming attachments are created. */
857     public static final String ATTACHMENTS_DIR   = "attachments.directory" ;
858 
859     /** A boolean param, to control whether we accept missing parameters
860      * as nulls or refuse to acknowledge them.
861      */
862     public final static String ACCEPTMISSINGPARAMS = "acceptMissingParams";
863 
864     /** The value of the property is used by service WSDL generation (aka ?WSDL)
865      * For the service's interface namespace if not set TRANS_URL property is used.
866      */
867     public static final String WSDLGEN_INTFNAMESPACE      = "axis.wsdlgen.intfnamespace";
868 
869     /** The value of the property is used by service WSDL generation (aka ?WSDL).
870      * For the service's location if not set TRANS_URL property is used.
871      *  (helps provide support through proxies.
872      */
873     public static final String WSDLGEN_SERV_LOC_URL      = "axis.wsdlgen.serv.loc.url";
874 
875     // fixme: should this be a type-safe e-num?
876     /** The value of the property is used by service WSDL generation (aka ?WSDL).
877      *  Set this property to request a certain level of HTTP.
878      *  The values MUST use org.apache.axis.transport.http.HTTPConstants.HEADER_PROTOCOL_10
879      *    for HTTP 1.0
880      *  The values MUST use org.apache.axis.transport.http.HTTPConstants.HEADER_PROTOCOL_11
881      *    for HTTP 1.1
882      */
883     public static final String HTTP_TRANSPORT_VERSION  = "axis.transport.version";
884 
885     // fixme: is this the name of a security provider, or the name of a security
886     //  provider class, or the actualy class of a security provider, or
887     //  something else?
888     /**
889      * The security provider.
890      */
891     public static final String SECURITY_PROVIDER = "securityProvider";
892 
893     /*
894      * IMPORTANT.
895      * If adding any new constants to this class. Make them final. The
896      * ones above are left non-final for compatibility reasons.
897      */
898 
899     /**
900      * Get a <code>String</code> property by name.
901      *
902      * @param propName the name of the property to fetch
903      * @return the value of the named property
904      * @throws ClassCastException if the property named does not have a
905      *              <code>String</code> value
906      */
907     public String getStrProp(String propName) {
908         return (String) getProperty(propName);
909     }
910 
911     /**
912      * Tests to see if the named property is set in the 'bag', returning
913      * <code>false</code> if it is not present at all.
914      * This is equivalent to <code>isPropertyTrue(propName, false)</code>.
915      *
916      * @param propName  the name of the property to check
917      * @return true or false, depending on the value of the property
918      */
919     public boolean isPropertyTrue(String propName) {
920         return isPropertyTrue(propName, false);
921     }
922 
923     /**
924      * Test if a property is set to something we consider to be true in the
925      * 'bag'.
926      * <ul>
927      * <li>If not there then <code>defaultVal</code> is returned.</li>
928      * <li>If there, then...<ul>
929      *   <li>if its a <code>Boolean</code>, we'll return booleanValue()</li>
930      *   <li>if its an <code>Integer</code>,  we'll return <code>false</code>
931      *   if its <code>0</code> else <code>true</code></li>
932      *   <li>if its a <code>String</code> we'll return <code>false</code> if its
933      *   <code>"false"</code>" or <code>"0"</code> else <code>true</code></li>
934      *   <li>All other types return <code>true</code></li>
935      * </ul></li>
936      * </ul>
937      *
938      * @param propName  the name of the property to check
939      * @param defaultVal  the default value
940      * @return true or false, depending on the value of the property
941      */
942     public boolean isPropertyTrue(String propName, boolean defaultVal) {
943         return JavaUtils.isTrue(getProperty(propName), defaultVal);
944     }
945 
946     /**
947      * Allows you to set a named property to the passed in value.
948      * There are a few known properties (like username, password, etc)
949      * that are variables in Call.  The rest of the properties are
950      * stored in a Hashtable.  These common properties should be
951      * accessed via the accessors for speed/type safety, but they may
952      * still be obtained via this method.  It's up to one of the
953      * Handlers (or the Axis engine itself) to go looking for
954      * one of them.
955      *
956      * @param name  Name of the property
957      * @param value Value of the property
958      */
959     public void setProperty(String name, Object value) {
960         if (name == null || value == null) {
961             return;
962             // Is this right?  Shouldn't we throw an exception like:
963             // throw new IllegalArgumentException(msg);
964         }
965         else if (name.equals(Call.USERNAME_PROPERTY)) {
966             if (!(value instanceof String)) {
967                 throw new IllegalArgumentException(
968                         Messages.getMessage("badProp00", new String[] {
969                         name, "java.lang.String", value.getClass().getName()}));
970             }
971             setUsername((String) value);
972         }
973         else if (name.equals(Call.PASSWORD_PROPERTY)) {
974             if (!(value instanceof String)) {
975                 throw new IllegalArgumentException(
976                         Messages.getMessage("badProp00", new String[] {
977                         name, "java.lang.String", value.getClass().getName()}));
978             }
979             setPassword((String) value);
980         }
981         else if (name.equals(Call.SESSION_MAINTAIN_PROPERTY)) {
982             if (!(value instanceof Boolean)) {
983                 throw new IllegalArgumentException(
984                         Messages.getMessage("badProp00", new String[]
985                         {name,
986                         "java.lang.Boolean",
987                         value.getClass().getName()}));
988             }
989             setMaintainSession(((Boolean) value).booleanValue());
990         }
991         else if (name.equals(Call.SOAPACTION_USE_PROPERTY)) {
992             if (!(value instanceof Boolean)) {
993                 throw new IllegalArgumentException(
994                         Messages.getMessage("badProp00", new String[]
995                         {name,
996                         "java.lang.Boolean",
997                         value.getClass().getName()}));
998             }
999             setUseSOAPAction(((Boolean) value).booleanValue());
1000        }
1001        else if (name.equals(Call.SOAPACTION_URI_PROPERTY)) {
1002            if (!(value instanceof String)) {
1003                throw new IllegalArgumentException(
1004                        Messages.getMessage("badProp00", new String[]
1005                        {name,
1006                        "java.lang.String",
1007                        value.getClass().getName()}));
1008            }
1009            setSOAPActionURI((String) value);
1010        }
1011        else if (name.equals(Call.ENCODINGSTYLE_URI_PROPERTY)) {
1012            if (!(value instanceof String)) {
1013                throw new IllegalArgumentException(
1014                        Messages.getMessage("badProp00", new String[]
1015                        {name,
1016                        "java.lang.String",
1017                        value.getClass().getName()}));
1018            }
1019            setEncodingStyle((String) value);
1020        }
1021        else {
1022            bag.put(name, value);
1023        }
1024    } // setProperty
1025
1026    /**
1027     *  Returns true if the MessageContext contains a property with the specified name.
1028     *  @param   name Name of the property whose presense is to be tested
1029     *  @return  Returns true if the MessageContext contains the
1030          property; otherwise false
1031     */
1032    public boolean containsProperty(String name) {
1033        Object propertyValue = getProperty(name);
1034        return (propertyValue != null);
1035    }
1036
1037    /**
1038     * Returns an <code>Iterator</code> view of the names of the properties in
1039     * this <code>MessageContext</code>.
1040     *
1041     * @return an <code>Iterator</code> over all property names
1042     */
1043    public java.util.Iterator getPropertyNames() {
1044        // fixme: this is potentially unsafe for the caller - changing the
1045        //  properties will kill the iterator. Consider iterating over a copy:
1046        // return new HashSet(bag.keySet()).iterator();
1047        return bag.keySet().iterator();
1048    }
1049
1050    /**
1051     *  Returns an Iterator view of the names of the properties 
1052     *  in this MessageContext and any parents of the LockableHashtable
1053     *  @return Iterator for the property names
1054     */
1055    public java.util.Iterator getAllPropertyNames() {
1056        return bag.getAllKeys().iterator();
1057    }
1058
1059    /**
1060     * Returns the value associated with the named property - or null if not
1061     * defined/set.
1062     *
1063     * @param name  the property name
1064     * @return Object value of the property - or null
1065     */
1066    public Object getProperty(String name) {
1067        if (name != null) {
1068            if (name.equals(Call.USERNAME_PROPERTY)) {
1069                return getUsername();
1070            }
1071            else if (name.equals(Call.PASSWORD_PROPERTY)) {
1072                return getPassword();
1073            }
1074            else if (name.equals(Call.SESSION_MAINTAIN_PROPERTY)) {
1075                return getMaintainSession() ? Boolean.TRUE : Boolean.FALSE;
1076            }
1077            else if (name.equals(Call.OPERATION_STYLE_PROPERTY)) {
1078                return (getOperationStyle() == null) ? null : getOperationStyle().getName();
1079            }
1080            else if (name.equals(Call.SOAPACTION_USE_PROPERTY)) {
1081                return useSOAPAction() ? Boolean.TRUE : Boolean.FALSE;
1082            }
1083            else if (name.equals(Call.SOAPACTION_URI_PROPERTY)) {
1084                return getSOAPActionURI();
1085            }
1086            else if (name.equals(Call.ENCODINGSTYLE_URI_PROPERTY)) {
1087                return getEncodingStyle();
1088            }
1089            else if (bag == null) {
1090                return null;
1091            }
1092            else {
1093                return bag.get(name);
1094            }
1095        }
1096        else {
1097            return null;
1098        }
1099    }
1100
1101    // fixme: this makes no copy of parent, so later modifications to parent
1102    //  can alter this context - is this intended? If so, it needs documenting.
1103    //  If not, it needs fixing.
1104    /**
1105     * Set the Hashtable that contains the default values for our
1106     * properties.
1107     *
1108     * @param parent
1109     */
1110    public void setPropertyParent(Hashtable parent)
1111    {
1112        bag.setParent(parent);
1113    }
1114
1115    /**
1116     * Set the username.
1117     *
1118     * @param username  the new user name
1119     */
1120    public void setUsername(String username) {
1121        this.username = username;
1122    } // setUsername
1123
1124    /**
1125     * Get the user name.
1126     *
1127     * @return the user name as a <code>String</code>
1128     */
1129    public String getUsername() {
1130        return username;
1131    } // getUsername
1132
1133    /**
1134     * Set the password.
1135     *
1136     * @param password  a <code>String</code> containing the new password
1137     */
1138    public void setPassword(String password) {
1139        this.password = password;
1140    } // setPassword
1141
1142    /**
1143     * Get the password.
1144     *
1145     * @return the current password <code>String</code>
1146     */
1147    public String getPassword() {
1148        return password;
1149    } // getPassword
1150
1151    /**
1152     * Get the operation style. This is either the style of the current
1153     * operation or if that is not set, the style of the service handler, or
1154     * if that is not set, <code>Style.RPC</code>.
1155     *
1156     * @return the <code>Style</code> of this message
1157     */
1158    public Style getOperationStyle() {
1159        if (currentOperation != null) {
1160            return currentOperation.getStyle();
1161        }
1162
1163        if (serviceHandler != null) {
1164            return serviceHandler.getStyle();
1165        }
1166
1167        return Style.RPC;
1168    } // getOperationStyle
1169
1170    /**
1171     * Get the operation use.
1172     *
1173     * @return the operation <code>Use</code>
1174     */
1175    public Use getOperationUse() {
1176        if (currentOperation != null) {
1177            return currentOperation.getUse();
1178        }
1179
1180        if (serviceHandler != null) {
1181            return serviceHandler.getUse();
1182        }
1183
1184        return Use.ENCODED;
1185    } // getOperationUse
1186
1187    /**
1188     * Enable or dissable the use of soap action information. When enabled,
1189     * the message context will attempt to use the soap action URI
1190     * information during binding of soap messages to service methods. When
1191     * dissabled, it will make no such attempt.
1192     *
1193     * @param useSOAPAction  <code>true</code> if soap action URI information
1194     *              should be used, <code>false</code> otherwise
1195     */
1196    public void setUseSOAPAction(boolean useSOAPAction) {
1197        this.useSOAPAction = useSOAPAction;
1198    } // setUseSOAPAction
1199
1200    // fixme: this doesn't follow beany naming conventions - should be
1201    //  isUseSOAPActions or getUseSOAPActions or something prettier
1202    /**
1203     * Indicates wether the soap action URI is being used or not.
1204     *
1205     * @return <code>true</code> if it is, <code>false</code> otherwise
1206     */
1207    public boolean useSOAPAction() {
1208        return useSOAPAction;
1209    } // useSOAPAction
1210
1211    // fixme: this throws IllegalArgumentException but never raises it -
1212    //  perhaps in a sub-class?
1213    // fixme: IllegalArgumentException is unchecked. Best practice says you
1214    //  should document unchecked exceptions, but not list them in throws
1215    /**
1216     * Set the soapAction URI.
1217     *
1218     * @param SOAPActionURI  a <code>String</code> giving the new soap action
1219     *              URI
1220     * @throws IllegalArgumentException if the URI is not liked
1221     */
1222    public void setSOAPActionURI(String SOAPActionURI)
1223            throws IllegalArgumentException {
1224        this.SOAPActionURI = SOAPActionURI;
1225    } // setSOAPActionURI
1226
1227    /**
1228     * Get the soapAction URI.
1229     *
1230     * @return the URI of this soap action
1231     */
1232    public String getSOAPActionURI() {
1233        return SOAPActionURI;
1234    } // getSOAPActionURI
1235
1236    /**
1237     * Sets the encoding style to the URI passed in.
1238     *
1239     * @param namespaceURI URI of the encoding to use.
1240     */
1241    public void setEncodingStyle(String namespaceURI) {
1242        if (namespaceURI == null) {
1243            namespaceURI = Constants.URI_LITERAL_ENC;
1244        }
1245        else if (Constants.isSOAP_ENC(namespaceURI)) {
1246            namespaceURI = soapConstants.getEncodingURI();
1247        }
1248
1249        encodingStyle = namespaceURI;
1250    } // setEncodingStype
1251
1252    /**
1253     * Returns the encoding style as a URI that should be used for the SOAP
1254     * message.
1255     *
1256     * @return String URI of the encoding style to use
1257     */
1258    public String getEncodingStyle() {
1259        return encodingStyle;
1260    } // getEncodingStyle
1261
1262    public void removeProperty(String propName)
1263    {
1264        if (bag != null) {
1265            bag.remove(propName);
1266        }
1267    }
1268
1269    /**
1270     * Return this context to a clean state.
1271     */
1272    public void reset()
1273    {
1274        if (bag != null) {
1275            bag.clear();
1276        }
1277        serviceHandler = null;
1278        havePassedPivot = false;
1279        currentOperation = null;
1280    }
1281
1282    /**
1283     * Read the high fidelity property.
1284     * <p>
1285     * Some behavior may be apropreate for high fidelity contexts that is not
1286     * relevant for low fidelity ones or vica-versa.
1287     *
1288     * @return <code>true</code> if the context is high fidelity,
1289     *              <code>false</code> otherwise
1290     */
1291    public boolean isHighFidelity() {
1292        return highFidelity;
1293    }
1294
1295    /**
1296     * Set the high fidelity propert.
1297     * <p>
1298     * Users of the context may be changing what they do based upon this flag.
1299     *
1300     * @param highFidelity  the new value of the highFidelity property
1301     */
1302    public void setHighFidelity(boolean highFidelity) {
1303        this.highFidelity = highFidelity;
1304    }
1305
1306    /**
1307     * Gets the SOAP actor roles associated with an execution of the
1308     * <code>HandlerChain</code> and its contained <code>Handler</code>
1309     * instances.
1310     * <p>
1311     * <i>Not (yet) implemented method in the SOAPMessageContext interface</i>.
1312     * <p>
1313     * <b>Note:</b> SOAP actor roles apply to the SOAP node and are managed
1314     * using <code>HandlerChain.setRoles()</code> and
1315     * <code>HandlerChain.getRoles()</code>. Handler instances in the
1316     * <code>HandlerChain</code> use this information about the SOAP actor roles
1317     * to process the SOAP header blocks. Note that the SOAP actor roles are
1318     * invariant during the processing of SOAP message through the
1319     * <code>HandlerChain</code>.
1320     *
1321     * @return an array of URIs for SOAP actor roles
1322     * @see javax.xml.rpc.handler.HandlerChain#setRoles(java.lang.String[]) HandlerChain.setRoles(java.lang.String[])
1323     * @see javax.xml.rpc.handler.HandlerChain#getRoles() HandlerChain.getRoles()
1324     */
1325    public String[] getRoles() {
1326        //TODO: Flesh this out.
1327        return roles;
1328    }
1329
1330    /**
1331     * Set the SOAP actor roles associated with an executioni of
1332     * <code>CodeHandlerChain</code> and its contained <code>Handler</code>
1333     * instances.
1334     *
1335     * @param roles an array of <code>String</code> instances, each representing
1336     *              the URI for a SOAP actor role
1337     */
1338    public void setRoles( String[] roles) {
1339        this.roles = roles;
1340    }
1341
1342    /**
1343     * if a message (or subclass) has any disposal needs, this method
1344     * is where it goes. Subclasses *must* call super.dispose(), and
1345     * be prepared to be called from the finalizer as well as earlier
1346     */
1347    public synchronized void dispose() {
1348        log.debug("disposing of message context");
1349        if(requestMessage!=null) {
1350            requestMessage.dispose();
1351            requestMessage=null;
1352        }
1353        if(responseMessage!=null) {
1354            responseMessage.dispose();
1355            responseMessage=null;
1356        }
1357    }
1358}