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}