blob: 47fdf1c4298028d0029767730749a87e4a5d4cd3 [file] [log] [blame]
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.axis ;
import org.apache.axis.attachments.Attachments;
import org.apache.axis.client.AxisClient;
import org.apache.axis.components.logger.LogFactory;
import org.apache.axis.description.OperationDesc;
import org.apache.axis.description.ServiceDesc;
import org.apache.axis.encoding.TypeMapping;
import org.apache.axis.encoding.TypeMappingRegistry;
import org.apache.axis.constants.Style;
import org.apache.axis.constants.Use;
import org.apache.axis.handlers.soap.SOAPService;
import org.apache.axis.schema.SchemaVersion;
import org.apache.axis.session.Session;
import org.apache.axis.soap.SOAPConstants;
import org.apache.axis.utils.JavaUtils;
import org.apache.axis.utils.LockableHashtable;
import org.apache.axis.utils.Messages;
import org.apache.commons.logging.Log;
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import java.io.File;
import java.util.ArrayList;
import java.util.Hashtable;
// fixme: fields are declared throughout this class, some at the top, and some
// near to where they are used. We should move all field declarations into a
// single block - it makes it easier to see what is decalred in this class and
// what is inherited. It also makes it easier to find them.
/**
* A MessageContext is the Axis implementation of the javax
* SOAPMessageContext class, and is core to message processing
* in handlers and other parts of the system.
*
* This class also contains constants for accessing some
* well-known properties. Using a hierarchical namespace is
* strongly suggested in order to lower the chance for
* conflicts.
*
* (These constants should be viewed as an explicit list of well
* known and widely used context keys, there's nothing wrong
* with directly using the key strings. This is the reason for
* the hierarchical constant namespace.
*
* Actually I think we might just list the keys in the docs and
* provide no such constants since they create yet another
* namespace, but we'd have no compile-time checks then.
*
* Whaddya think? - todo by Jacek)
*
*
* @author Doug Davis (dug@us.ibm.com)
* @author Jacek Kopecky (jacek@idoox.com)
*/
public class MessageContext implements SOAPMessageContext {
/** The <code>Log</code> used for logging all messages. */
protected static Log log =
LogFactory.getLog(MessageContext.class.getName());
/**
* The request message. If we're on the client, this is the outgoing
* message heading to the server. If we're on the server, this is the
* incoming message we've received from the client.
*/
private Message requestMessage;
/**
* The response message. If we're on the server, this is the outgoing
* message heading back to the client. If we're on the client, this is the
* incoming message we've received from the server.
*/
private Message responseMessage;
/**
* That unique key/name that the next router/dispatch handler should use
* to determine what to do next.
*/
private String targetService;
/**
* The name of the Transport which this message was received on (or is
* headed to, for the client).
*/
private String transportName;
/**
* The default <code>ClassLoader</code> that this service should use.
*/
private ClassLoader classLoader;
/**
* The AxisEngine which this context is involved with.
*/
private AxisEngine axisEngine;
/**
* A Session associated with this request.
*/
private Session session;
/**
* Should we track session state, or not?
* default is not.
* Could potentially refactor this so that
* maintainSession iff session != null...
*/
private boolean maintainSession = false;
// fixme: ambiguity here due to lac of docs - havePassedPivot vs
// request/response, vs sending/processing & recieving/responding
// I may have just missed the key bit of text
/**
* Are we doing request stuff, or response stuff? True if processing
* response (I think).
*/
private boolean havePassedPivot = false;
/**
* Maximum amount of time to wait on a request, in milliseconds.
*/
private int timeout = Constants.DEFAULT_MESSAGE_TIMEOUT;
/**
* An indication of whether we require "high fidelity" recording of
* deserialized messages for this interaction. Defaults to true for
* now, and can be set to false, usually at service-dispatch time.
*/
private boolean highFidelity = true;
/**
* Storage for an arbitrary bag of properties associated with this
* MessageContext.
*/
private LockableHashtable bag = new LockableHashtable();
/*
* These variables are logically part of the bag, but are separated
* because they are used often and the Hashtable is more expensive.
*
* fixme: this may be fixed by moving to a plain Map impl like HashMap.
* Alternatively, we could hide all this magic behind a custom Map impl -
* is synchronization on the map needed? these properties aren't
* synchronized so I'm guessing not.
*/
private String username = null;
private String password = null;
private String encodingStyle = Use.ENCODED.getEncoding();
private boolean useSOAPAction = false;
private String SOAPActionURI = null;
/**
* SOAP Actor roles.
*/
private String[] roles;
/** Our SOAP namespaces and such. */
private SOAPConstants soapConstants = Constants.DEFAULT_SOAP_VERSION;
/** Schema version information - defaults to 2001. */
private SchemaVersion schemaVersion = SchemaVersion.SCHEMA_2001;
/** Our current operation. */
private OperationDesc currentOperation = null;
/**
* The current operation.
*
* @return the current operation; may be <code>null</code>
*/
public OperationDesc getOperation()
{
return currentOperation;
}
/**
* Set the current operation.
*
* @param operation the <code>Operation</code> this context is executing
*/
public void setOperation(OperationDesc operation)
{
currentOperation = operation;
}
/**
* Returns a list of operation descriptors that could may
* possibly match a body containing an element of the given QName.
* For non-DOCUMENT, the list of operation descriptors that match
* the name is returned. For DOCUMENT, all the operations that have
* qname as a parameter are returned
*
* @param qname of the first element in the body
* @return list of operation descriptions
* @throws AxisFault if the operation names could not be looked up
*/
public OperationDesc [] getPossibleOperationsByQName(QName qname) throws AxisFault
{
if (currentOperation != null) {
return new OperationDesc [] { currentOperation };
}
OperationDesc [] possibleOperations = null;
if (serviceHandler == null) {
try {
if (log.isDebugEnabled()) {
log.debug(Messages.getMessage("dispatching00",
qname.getNamespaceURI()));
}
// Try looking this QName up in our mapping table...
setService(axisEngine.getConfig().
getServiceByNamespaceURI(qname.getNamespaceURI()));
} catch (ConfigurationException e) {
// Didn't find one...
}
}
if (serviceHandler != null) {
ServiceDesc desc = serviceHandler.getInitializedServiceDesc(this);
if (desc != null) {
if (desc.getStyle() != Style.DOCUMENT) {
possibleOperations = desc.getOperationsByQName(qname);
} else {
// DOCUMENT Style
// Get all of the operations that have qname as
// a possible parameter QName
ArrayList allOperations = desc.getOperations();
ArrayList foundOperations = new ArrayList();
for (int i=0; i < allOperations.size(); i++ ) {
OperationDesc tryOp =
(OperationDesc) allOperations.get(i);
if (tryOp.getParamByQName(qname) != null) {
foundOperations.add(tryOp);
}
}
if (foundOperations.size() > 0) {
possibleOperations = (OperationDesc[])
JavaUtils.convert(foundOperations,
OperationDesc[].class);
}
}
}
}
return possibleOperations;
}
/**
* get the first possible operation that could match a
* body containing an element of the given QName. Sets the currentOperation
* field in the process; if that field is already set then its value
* is returned instead
* @param qname name of the message body
* @return an operation or null
* @throws AxisFault
*/
public OperationDesc getOperationByQName(QName qname) throws AxisFault
{
if (currentOperation == null) {
OperationDesc [] possibleOperations = getPossibleOperationsByQName(qname);
if (possibleOperations != null && possibleOperations.length > 0) {
currentOperation = possibleOperations[0];
}
}
return currentOperation;
}
/**
* Get the active message context.
*
* @return the current active message context
*/
public static MessageContext getCurrentContext() {
return AxisEngine.getCurrentMessageContext();
}
/**
* Temporary directory to store attachments.
*/
protected static String systemTempDir= null;
/**
* set the temp dir
* TODO: move this piece of code out of this class and into a utilities
* class.
*/
static {
try {
//get the temp dir from the engine
systemTempDir=AxisProperties.getProperty(AxisEngine.ENV_ATTACHMENT_DIR);
} catch(Throwable t) {
systemTempDir= null;
}
if(systemTempDir== null) {
try {
//or create and delete a file in the temp dir to make
//sure we have write access to it.
File tf= File.createTempFile("Axis", ".tmp");
File dir= tf.getParentFile();
if (tf.exists()) {
tf.delete();
}
if (dir != null) {
systemTempDir= dir.getCanonicalPath();
}
} catch(Throwable t) {
log.debug("Unable to find a temp dir with write access");
systemTempDir= null;
}
}
}
/**
* Create a message context.
* @param engine the controlling axis engine. Null is actually accepted here,
* though passing a null engine in is strongly discouraged as many of the methods
* assume that it is in fact defined.
*/
public MessageContext(AxisEngine engine) {
this.axisEngine = engine;
if(null != engine){
java.util.Hashtable opts= engine.getOptions();
String attachmentsdir= null;
if(null!=opts) {
attachmentsdir= (String) opts.get(AxisEngine.PROP_ATTACHMENT_DIR);
}
if(null == attachmentsdir) {
attachmentsdir= systemTempDir;
}
if(attachmentsdir != null){
setProperty(ATTACHMENTS_DIR, attachmentsdir);
}
// If SOAP 1.2 has been specified as the default for the engine,
// switch the constants over.
String defaultSOAPVersion = (String)engine.getOption(
AxisEngine.PROP_SOAP_VERSION);
if (defaultSOAPVersion != null && "1.2".equals(defaultSOAPVersion)) {
setSOAPConstants(SOAPConstants.SOAP12_CONSTANTS);
}
String singleSOAPVersion = (String)engine.getOption(
AxisEngine.PROP_SOAP_ALLOWED_VERSION);
if (singleSOAPVersion != null) {
if ("1.2".equals(singleSOAPVersion)) {
setProperty(Constants.MC_SINGLE_SOAP_VERSION,
SOAPConstants.SOAP12_CONSTANTS);
} else if ("1.1".equals(singleSOAPVersion)) {
setProperty(Constants.MC_SINGLE_SOAP_VERSION,
SOAPConstants.SOAP11_CONSTANTS);
}
}
}
}
/**
* during finalization, the dispose() method is called.
* @see #dispose()
*/
protected void finalize() {
dispose();
}
/**
* Mappings of QNames to serializers/deserializers (and therfore
* to Java types).
*/
private TypeMappingRegistry mappingRegistry = null;
/**
* Replace the engine's type mapping registry with a local one. This will
* have no effect on any type mappings obtained before this call.
*
* @param reg the new <code>TypeMappingRegistry</code>
*/
public void setTypeMappingRegistry(TypeMappingRegistry reg) {
mappingRegistry = reg;
}
/**
* Get the currently in-scope type mapping registry.
*
* By default, will return a reference to the AxisEngine's TMR until
* someone sets our local one (usually as a result of setting the
* serviceHandler).
*
* @return the type mapping registry to use for this request.
*/
public TypeMappingRegistry getTypeMappingRegistry() {
if (mappingRegistry == null) {
return axisEngine.getTypeMappingRegistry();
}
return mappingRegistry;
}
/**
* Return the type mapping currently in scope for our encoding style.
*
* @return the type mapping
*/
public TypeMapping getTypeMapping()
{
return (TypeMapping)getTypeMappingRegistry().
getTypeMapping(encodingStyle);
}
/**
* The name of the transport for this context.
*
* @return the transport name
*/
public String getTransportName()
{
return transportName;
}
// fixme: the transport names should be a type-safe e-num, or the range
// of legal values should be specified in the documentation and validated
// in the method (raising IllegalArgumentException)
/**
* Set the transport name for this context.
*
* @param transportName the name of the transport
*/
public void setTransportName(String transportName)
{
this.transportName = transportName;
}
/**
* Get the <code>SOAPConstants</code> used by this message context.
*
* @return the soap constants
*/
public SOAPConstants getSOAPConstants() {
return soapConstants;
}
/**
* Set the <code>SOAPConstants</code> used by this message context.
* This may also affect the encoding style.
*
* @param soapConstants the new soap constants to use
*/
public void setSOAPConstants(SOAPConstants soapConstants) {
// when changing SOAP versions, remember to keep the encodingURI
// in synch.
if (this.soapConstants.getEncodingURI().equals(encodingStyle)) {
encodingStyle = soapConstants.getEncodingURI();
}
this.soapConstants = soapConstants;
}
/**
* Get the XML schema version information.
*
* @return the <code>SchemaVersion</code> in use
*/
public SchemaVersion getSchemaVersion() {
return schemaVersion;
}
/**
* Set the XML schema version this message context will use.
*
* @param schemaVersion the new <code>SchemaVersion</code>
*/
public void setSchemaVersion(SchemaVersion schemaVersion) {
this.schemaVersion = schemaVersion;
}
/**
* Get the current session.
*
* @return the <code>Session</code> this message context is within
*/
public Session getSession()
{
return session;
}
/**
* Set the current session.
*
* @param session the new <code>Session</code>
*/
public void setSession(Session session)
{
this.session = session;
}
/**
* Indicates if the opration is encoded.
*
* @return <code>true</code> if it is encoded, <code>false</code> otherwise
*/
public boolean isEncoded() {
return (getOperationUse() == Use.ENCODED);
//return soapConstants.getEncodingURI().equals(encodingStyle);
}
/**
* Set whether we are maintaining session state.
*
* @param yesno flag to set to <code>true</code> to maintain sessions
*/
public void setMaintainSession (boolean yesno) {
maintainSession = yesno;
}
/**
* Discover if we are maintaining session state.
*
* @return <code>true</code> if we are maintaining state, <code>false</code>
* otherwise
*/
public boolean getMaintainSession () {
return maintainSession;
}
/**
* Get the request message.
*
* @return the request message (may be null).
*/
public Message getRequestMessage() {
return requestMessage ;
}
/**
* Set the request message, and make sure that message is associated
* with this MessageContext.
*
* @param reqMsg the new request Message.
*/
public void setRequestMessage(Message reqMsg) {
requestMessage = reqMsg ;
if (requestMessage != null) {
requestMessage.setMessageContext(this);
}
}
/**
* Get the response message.
*
* @return the response message (may be null).
*/
public Message getResponseMessage() { return responseMessage ; }
/**
* Set the response message, and make sure that message is associated
* with this MessageContext.
*
* @param respMsg the new response Message.
*/
public void setResponseMessage(Message respMsg) {
responseMessage = respMsg;
if (responseMessage != null) {
responseMessage.setMessageContext(this);
//if we have received attachments of a particular type
// than that should be the default type to send.
Message reqMsg = getRequestMessage();
if (null != reqMsg) {
Attachments reqAttch = reqMsg.getAttachmentsImpl();
Attachments respAttch = respMsg.getAttachmentsImpl();
if (null != reqAttch && null != respAttch) {
if (respAttch.getSendType() == Attachments.SEND_TYPE_NOTSET)
//only if not explicity set.
respAttch.setSendType(reqAttch.getSendType());
}
}
}
}
/**
* Return the current (i.e. request before the pivot, response after)
* message.
*
* @return the current <code>Message</code>
*/
public Message getCurrentMessage()
{
return (havePassedPivot ? responseMessage : requestMessage);
}
/**
* Gets the SOAPMessage from this message context.
*
* @return the <code>SOAPMessage</code>, <code>null</code> if no request
* <code>SOAPMessage</code> is present in this
* <code>SOAPMessageContext</code>
*/
public javax.xml.soap.SOAPMessage getMessage() {
return getCurrentMessage();
}
/**
* Set the current message. This will set the request before the pivot,
* and the response afterwards, as guaged by the passedPivod property.
*
* @param curMsg the <code>Message</code> to assign
*/
public void setCurrentMessage(Message curMsg)
{
if ( curMsg != null )
curMsg.setMessageContext(this);
if (havePassedPivot) {
responseMessage = curMsg;
} else {
requestMessage = curMsg;
}
}
/**
* Sets the SOAPMessage for this message context.
* This is equivalent to casting <code>message</code> to
* <code>Message</code> and then passing it on to
* <code>setCurrentMessage()</code>.
*
* @param message the <code>SOAPMessage</code> this context is for
*/
public void setMessage(javax.xml.soap.SOAPMessage message) {
setCurrentMessage((Message)message);
}
/**
* Determine when we've passed the pivot.
*
* @return <code>true</code> if we have, <code>false</code> otherwise
*/
public boolean getPastPivot()
{
return havePassedPivot;
}
// fixme: is there any legitimate case where we could pass the pivot and
// then go back again? Is there documentation about the life-cycle of a
// MessageContext, and in particular the re-use of instances that would be
// relevant?
/**
* Indicate when we've passed the pivot.
*
* @param pastPivot true if we are past the pivot point, false otherwise
*/
public void setPastPivot(boolean pastPivot)
{
havePassedPivot = pastPivot;
}
/**
* Set timeout in our MessageContext.
*
* @param value the maximum amount of time, in milliseconds
*/
public void setTimeout (int value) {
timeout = value;
}
/**
* Get timeout from our MessageContext.
*
* @return value the maximum amount of time, in milliseconds
*/
public int getTimeout () {
return timeout;
}
/**
* Get the classloader, implicitly binding to the thread context
* classloader if an override has not been supplied.
*
* @return the class loader
*/
public ClassLoader getClassLoader() {
if ( classLoader == null ) {
classLoader = Thread.currentThread().getContextClassLoader();
}
return( classLoader );
}
/**
* Set a new classloader. Setting to null will result in getClassLoader()
* binding back to the thread context class loader.
*
* @param cl the new <code>ClassLoader</code> or <code>null</code>
*/
public void setClassLoader(ClassLoader cl ) {
classLoader = cl ;
}
/**
* Get the name of the targed service for this message.
*
* @return the target service
*/
public String getTargetService() {
return targetService;
}
/**
* Get the axis engine. This will be <code>null</code> if the message was
* created outside an engine
*
* @return the current axis engine
*/
public AxisEngine getAxisEngine()
{
return axisEngine;
}
/**
* Set the target service for this message.
* <p>
* This looks up the named service in the registry, and has
* the side effect of setting our TypeMappingRegistry to the
* service's.
*
* @param tServ the name of the target service
* @throws AxisFault if anything goes wrong in resolving or setting the
* service
*/
public void setTargetService(String tServ) throws AxisFault {
log.debug("MessageContext: setTargetService(" + tServ+")");
if (tServ == null) {
setService(null);
}
else {
try {
setService(getAxisEngine().getService(tServ));
} catch (AxisFault fault) {
// If we're on the client, don't throw this fault...
if (!isClient()) {
throw fault;
}
}
}
targetService = tServ;
}
/** ServiceHandler is the handler that is the "service". This handler
* can (and probably will actually be a chain that contains the
* service specific request/response/pivot point handlers
*/
private SOAPService serviceHandler ;
/**
* Get the <code>SOAPService</code> used to handle services in this
* context.
*
* @return the service handler
*/
public SOAPService getService() {
return serviceHandler;
}
/**
* Set the <code>SOAPService</code> used to handle services in this
* context. This method configures a wide range of
* <code>MessageContext</code> properties to suit the handler.
*
* @param sh the new service handler
* @throws AxisFault if the service could not be set
*/
public void setService(SOAPService sh) throws AxisFault
{
log.debug("MessageContext: setServiceHandler("+sh+")");
serviceHandler = sh;
if (sh != null) {
if(!sh.isRunning()) {
throw new AxisFault(Messages.getMessage("disabled00"));
}
targetService = sh.getName();
SOAPService service = sh;
TypeMappingRegistry tmr = service.getTypeMappingRegistry();
setTypeMappingRegistry(tmr);
// styles are not "soap version aware" so compensate...
setEncodingStyle(service.getUse().getEncoding());
// This MessageContext should now defer properties it can't find
// to the Service's options.
bag.setParent(sh.getOptions());
// Note that we need (or don't need) high-fidelity SAX recording
// of deserialized messages according to the setting on the
// new service.
highFidelity = service.needsHighFidelityRecording();
service.getInitializedServiceDesc(this);
}
}
/**
* Let us know whether this is the client or the server.
*
* @return true if we are a client
*/
public boolean isClient()
{
return (axisEngine instanceof AxisClient);
}
// fixme: public final statics tend to go in a block at the top of the
// class deffinition, not marooned in the middle
// fixme: chose public static final /or/ public final static
/** Contains an instance of Handler, which is the
* ServiceContext and the entrypoint of this service.
*
* (if it has been so configured - will our deployment
* tool do this by default? - todo by Jacek)
*/
public static final String ENGINE_HANDLER = "engine.handler";
/** This String is the URL that the message came to.
*/
public static final String TRANS_URL = "transport.url";
/** Has a quit been requested? Hackish... but useful... -- RobJ */
public static final String QUIT_REQUESTED = "quit.requested";
/** Place to store an AuthenticatedUser. */
public static final String AUTHUSER = "authenticatedUser";
/** If on the client - this is the Call object. */
public static final String CALL = "call_object" ;
/** Are we doing Msg vs RPC? - For Java Binding. */
public static final String IS_MSG = "isMsg" ;
/** The directory where in coming attachments are created. */
public static final String ATTACHMENTS_DIR = "attachments.directory" ;
/** A boolean param, to control whether we accept missing parameters
* as nulls or refuse to acknowledge them.
*/
public final static String ACCEPTMISSINGPARAMS = "acceptMissingParams";
/** The value of the property is used by service WSDL generation (aka ?WSDL)
* For the service's interface namespace if not set TRANS_URL property is used.
*/
public static final String WSDLGEN_INTFNAMESPACE = "axis.wsdlgen.intfnamespace";
/** The value of the property is used by service WSDL generation (aka ?WSDL).
* For the service's location if not set TRANS_URL property is used.
* (helps provide support through proxies.
*/
public static final String WSDLGEN_SERV_LOC_URL = "axis.wsdlgen.serv.loc.url";
// fixme: should this be a type-safe e-num?
/** The value of the property is used by service WSDL generation (aka ?WSDL).
* Set this property to request a certain level of HTTP.
* The values MUST use org.apache.axis.transport.http.HTTPConstants.HEADER_PROTOCOL_10
* for HTTP 1.0
* The values MUST use org.apache.axis.transport.http.HTTPConstants.HEADER_PROTOCOL_11
* for HTTP 1.1
*/
public static final String HTTP_TRANSPORT_VERSION = "axis.transport.version";
// fixme: is this the name of a security provider, or the name of a security
// provider class, or the actualy class of a security provider, or
// something else?
/**
* The security provider.
*/
public static final String SECURITY_PROVIDER = "securityProvider";
/*
* IMPORTANT.
* If adding any new constants to this class. Make them final. The
* ones above are left non-final for compatibility reasons.
*/
/**
* Get a <code>String</code> property by name.
*
* @param propName the name of the property to fetch
* @return the value of the named property
* @throws ClassCastException if the property named does not have a
* <code>String</code> value
*/
public String getStrProp(String propName) {
return (String) getProperty(propName);
}
/**
* Tests to see if the named property is set in the 'bag', returning
* <code>false</code> if it is not present at all.
* This is equivalent to <code>isPropertyTrue(propName, false)</code>.
*
* @param propName the name of the property to check
* @return true or false, depending on the value of the property
*/
public boolean isPropertyTrue(String propName) {
return isPropertyTrue(propName, false);
}
/**
* Test if a property is set to something we consider to be true in the
* 'bag'.
* <ul>
* <li>If not there then <code>defaultVal</code> is returned.</li>
* <li>If there, then...<ul>
* <li>if its a <code>Boolean</code>, we'll return booleanValue()</li>
* <li>if its an <code>Integer</code>, we'll return <code>false</code>
* if its <code>0</code> else <code>true</code></li>
* <li>if its a <code>String</code> we'll return <code>false</code> if its
* <code>"false"</code>" or <code>"0"</code> else <code>true</code></li>
* <li>All other types return <code>true</code></li>
* </ul></li>
* </ul>
*
* @param propName the name of the property to check
* @param defaultVal the default value
* @return true or false, depending on the value of the property
*/
public boolean isPropertyTrue(String propName, boolean defaultVal) {
return JavaUtils.isTrue(getProperty(propName), defaultVal);
}
/**
* Allows you to set a named property to the passed in value.
* There are a few known properties (like username, password, etc)
* that are variables in Call. The rest of the properties are
* stored in a Hashtable. These common properties should be
* accessed via the accessors for speed/type safety, but they may
* still be obtained via this method. It's up to one of the
* Handlers (or the Axis engine itself) to go looking for
* one of them.
*
* @param name Name of the property
* @param value Value of the property
*/
public void setProperty(String name, Object value) {
if (name == null || value == null) {
return;
// Is this right? Shouldn't we throw an exception like:
// throw new IllegalArgumentException(msg);
}
else if (name.equals(Call.USERNAME_PROPERTY)) {
if (!(value instanceof String)) {
throw new IllegalArgumentException(
Messages.getMessage("badProp00", new String[] {
name, "java.lang.String", value.getClass().getName()}));
}
setUsername((String) value);
}
else if (name.equals(Call.PASSWORD_PROPERTY)) {
if (!(value instanceof String)) {
throw new IllegalArgumentException(
Messages.getMessage("badProp00", new String[] {
name, "java.lang.String", value.getClass().getName()}));
}
setPassword((String) value);
}
else if (name.equals(Call.SESSION_MAINTAIN_PROPERTY)) {
if (!(value instanceof Boolean)) {
throw new IllegalArgumentException(
Messages.getMessage("badProp00", new String[]
{name,
"java.lang.Boolean",
value.getClass().getName()}));
}
setMaintainSession(((Boolean) value).booleanValue());
}
else if (name.equals(Call.SOAPACTION_USE_PROPERTY)) {
if (!(value instanceof Boolean)) {
throw new IllegalArgumentException(
Messages.getMessage("badProp00", new String[]
{name,
"java.lang.Boolean",
value.getClass().getName()}));
}
setUseSOAPAction(((Boolean) value).booleanValue());
}
else if (name.equals(Call.SOAPACTION_URI_PROPERTY)) {
if (!(value instanceof String)) {
throw new IllegalArgumentException(
Messages.getMessage("badProp00", new String[]
{name,
"java.lang.String",
value.getClass().getName()}));
}
setSOAPActionURI((String) value);
}
else if (name.equals(Call.ENCODINGSTYLE_URI_PROPERTY)) {
if (!(value instanceof String)) {
throw new IllegalArgumentException(
Messages.getMessage("badProp00", new String[]
{name,
"java.lang.String",
value.getClass().getName()}));
}
setEncodingStyle((String) value);
}
else {
bag.put(name, value);
}
} // setProperty
/**
* Returns true if the MessageContext contains a property with the specified name.
* @param name Name of the property whose presense is to be tested
* @return Returns true if the MessageContext contains the
property; otherwise false
*/
public boolean containsProperty(String name) {
Object propertyValue = getProperty(name);
return (propertyValue != null);
}
/**
* Returns an <code>Iterator</code> view of the names of the properties in
* this <code>MessageContext</code>.
*
* @return an <code>Iterator</code> over all property names
*/
public java.util.Iterator getPropertyNames() {
// fixme: this is potentially unsafe for the caller - changing the
// properties will kill the iterator. Consider iterating over a copy:
// return new HashSet(bag.keySet()).iterator();
return bag.keySet().iterator();
}
/**
* Returns an Iterator view of the names of the properties
* in this MessageContext and any parents of the LockableHashtable
* @return Iterator for the property names
*/
public java.util.Iterator getAllPropertyNames() {
return bag.getAllKeys().iterator();
}
/**
* Returns the value associated with the named property - or null if not
* defined/set.
*
* @param name the property name
* @return Object value of the property - or null
*/
public Object getProperty(String name) {
if (name != null) {
if (name.equals(Call.USERNAME_PROPERTY)) {
return getUsername();
}
else if (name.equals(Call.PASSWORD_PROPERTY)) {
return getPassword();
}
else if (name.equals(Call.SESSION_MAINTAIN_PROPERTY)) {
return getMaintainSession() ? Boolean.TRUE : Boolean.FALSE;
}
else if (name.equals(Call.OPERATION_STYLE_PROPERTY)) {
return (getOperationStyle() == null) ? null : getOperationStyle().getName();
}
else if (name.equals(Call.SOAPACTION_USE_PROPERTY)) {
return useSOAPAction() ? Boolean.TRUE : Boolean.FALSE;
}
else if (name.equals(Call.SOAPACTION_URI_PROPERTY)) {
return getSOAPActionURI();
}
else if (name.equals(Call.ENCODINGSTYLE_URI_PROPERTY)) {
return getEncodingStyle();
}
else if (bag == null) {
return null;
}
else {
return bag.get(name);
}
}
else {
return null;
}
}
// fixme: this makes no copy of parent, so later modifications to parent
// can alter this context - is this intended? If so, it needs documenting.
// If not, it needs fixing.
/**
* Set the Hashtable that contains the default values for our
* properties.
*
* @param parent
*/
public void setPropertyParent(Hashtable parent)
{
bag.setParent(parent);
}
/**
* Set the username.
*
* @param username the new user name
*/
public void setUsername(String username) {
this.username = username;
} // setUsername
/**
* Get the user name.
*
* @return the user name as a <code>String</code>
*/
public String getUsername() {
return username;
} // getUsername
/**
* Set the password.
*
* @param password a <code>String</code> containing the new password
*/
public void setPassword(String password) {
this.password = password;
} // setPassword
/**
* Get the password.
*
* @return the current password <code>String</code>
*/
public String getPassword() {
return password;
} // getPassword
/**
* Get the operation style. This is either the style of the current
* operation or if that is not set, the style of the service handler, or
* if that is not set, <code>Style.RPC</code>.
*
* @return the <code>Style</code> of this message
*/
public Style getOperationStyle() {
if (currentOperation != null) {
return currentOperation.getStyle();
}
if (serviceHandler != null) {
return serviceHandler.getStyle();
}
return Style.RPC;
} // getOperationStyle
/**
* Get the operation use.
*
* @return the operation <code>Use</code>
*/
public Use getOperationUse() {
if (currentOperation != null) {
return currentOperation.getUse();
}
if (serviceHandler != null) {
return serviceHandler.getUse();
}
return Use.ENCODED;
} // getOperationUse
/**
* Enable or dissable the use of soap action information. When enabled,
* the message context will attempt to use the soap action URI
* information during binding of soap messages to service methods. When
* dissabled, it will make no such attempt.
*
* @param useSOAPAction <code>true</code> if soap action URI information
* should be used, <code>false</code> otherwise
*/
public void setUseSOAPAction(boolean useSOAPAction) {
this.useSOAPAction = useSOAPAction;
} // setUseSOAPAction
// fixme: this doesn't follow beany naming conventions - should be
// isUseSOAPActions or getUseSOAPActions or something prettier
/**
* Indicates wether the soap action URI is being used or not.
*
* @return <code>true</code> if it is, <code>false</code> otherwise
*/
public boolean useSOAPAction() {
return useSOAPAction;
} // useSOAPAction
// fixme: this throws IllegalArgumentException but never raises it -
// perhaps in a sub-class?
// fixme: IllegalArgumentException is unchecked. Best practice says you
// should document unchecked exceptions, but not list them in throws
/**
* Set the soapAction URI.
*
* @param SOAPActionURI a <code>String</code> giving the new soap action
* URI
* @throws IllegalArgumentException if the URI is not liked
*/
public void setSOAPActionURI(String SOAPActionURI)
throws IllegalArgumentException {
this.SOAPActionURI = SOAPActionURI;
} // setSOAPActionURI
/**
* Get the soapAction URI.
*
* @return the URI of this soap action
*/
public String getSOAPActionURI() {
return SOAPActionURI;
} // getSOAPActionURI
/**
* Sets the encoding style to the URI passed in.
*
* @param namespaceURI URI of the encoding to use.
*/
public void setEncodingStyle(String namespaceURI) {
if (namespaceURI == null) {
namespaceURI = Constants.URI_LITERAL_ENC;
}
else if (Constants.isSOAP_ENC(namespaceURI)) {
namespaceURI = soapConstants.getEncodingURI();
}
encodingStyle = namespaceURI;
} // setEncodingStype
/**
* Returns the encoding style as a URI that should be used for the SOAP
* message.
*
* @return String URI of the encoding style to use
*/
public String getEncodingStyle() {
return encodingStyle;
} // getEncodingStyle
public void removeProperty(String propName)
{
if (bag != null) {
bag.remove(propName);
}
}
/**
* Return this context to a clean state.
*/
public void reset()
{
if (bag != null) {
bag.clear();
}
serviceHandler = null;
havePassedPivot = false;
currentOperation = null;
}
/**
* Read the high fidelity property.
* <p>
* Some behavior may be apropreate for high fidelity contexts that is not
* relevant for low fidelity ones or vica-versa.
*
* @return <code>true</code> if the context is high fidelity,
* <code>false</code> otherwise
*/
public boolean isHighFidelity() {
return highFidelity;
}
/**
* Set the high fidelity propert.
* <p>
* Users of the context may be changing what they do based upon this flag.
*
* @param highFidelity the new value of the highFidelity property
*/
public void setHighFidelity(boolean highFidelity) {
this.highFidelity = highFidelity;
}
/**
* Gets the SOAP actor roles associated with an execution of the
* <code>HandlerChain</code> and its contained <code>Handler</code>
* instances.
* <p>
* <i>Not (yet) implemented method in the SOAPMessageContext interface</i>.
* <p>
* <b>Note:</b> SOAP actor roles apply to the SOAP node and are managed
* using <code>HandlerChain.setRoles()</code> and
* <code>HandlerChain.getRoles()</code>. Handler instances in the
* <code>HandlerChain</code> use this information about the SOAP actor roles
* to process the SOAP header blocks. Note that the SOAP actor roles are
* invariant during the processing of SOAP message through the
* <code>HandlerChain</code>.
*
* @return an array of URIs for SOAP actor roles
* @see javax.xml.rpc.handler.HandlerChain#setRoles(java.lang.String[]) HandlerChain.setRoles(java.lang.String[])
* @see javax.xml.rpc.handler.HandlerChain#getRoles() HandlerChain.getRoles()
*/
public String[] getRoles() {
//TODO: Flesh this out.
return roles;
}
/**
* Set the SOAP actor roles associated with an executioni of
* <code>CodeHandlerChain</code> and its contained <code>Handler</code>
* instances.
*
* @param roles an array of <code>String</code> instances, each representing
* the URI for a SOAP actor role
*/
public void setRoles( String[] roles) {
this.roles = roles;
}
/**
* if a message (or subclass) has any disposal needs, this method
* is where it goes. Subclasses *must* call super.dispose(), and
* be prepared to be called from the finalizer as well as earlier
*/
public synchronized void dispose() {
log.debug("disposing of message context");
if(requestMessage!=null) {
requestMessage.dispose();
requestMessage=null;
}
if(responseMessage!=null) {
responseMessage.dispose();
responseMessage=null;
}
}
}