blob: 5e696629a75ef9df740e31fe9b40f2c14d6ca757 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.axis2.description;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Set;
import javax.xml.namespace.QName;
import org.apache.axiom.om.util.UUIDGenerator;
import org.apache.axis2.AxisFault;
import org.apache.axis2.client.OperationClient;
import org.apache.axis2.client.Options;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.context.OperationContext;
import org.apache.axis2.context.ServiceContext;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.engine.AxisError;
import org.apache.axis2.engine.MessageReceiver;
import org.apache.axis2.i18n.Messages;
import org.apache.axis2.phaseresolver.PhaseResolver;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public abstract class AxisOperation extends AxisDescription
implements WSDLConstants {
public static final String STYLE_RPC = "rpc";
public static final String STYLE_MSG = "msg";
public static final String STYLE_DOC = "doc";
private static final Log log = LogFactory.getLog(AxisOperation.class);
/** message exchange pattern */
private int mep = WSDLConstants.MEP_CONSTANT_INVALID;
// to hide control operation , operation which added by RM like module
private boolean controlOperation = false;
private String style = STYLE_DOC;
// to store mepURL
protected String mepURI;
private MessageReceiver messageReceiver;
private HashMap<String, ModuleConfiguration> moduleConfigmap;
// To store deploy-time module refs
private ArrayList<String> modulerefs;
private ArrayList<AxisMessage> faultMessages;
private QName name;
private ArrayList<String> wsamappingList;
private String outputAction;
private LinkedHashMap<String, String> faultActions = new LinkedHashMap<String, String>();
private String soapAction;
/** Default constructor */
public AxisOperation() {
mepURI = WSDL2Constants.MEP_URI_IN_OUT;
modulerefs = new ArrayList<String>();
moduleConfigmap = new HashMap<String, ModuleConfiguration>();
faultMessages = new ArrayList<AxisMessage>();
//setup a temporary name
QName tmpName = new QName(this.getClass().getName() + "_" + UUIDGenerator.getUUID());
this.setName(tmpName);
}
public AxisOperation(QName name) {
this();
this.setName(name);
}
public abstract void addMessage(AxisMessage message, String label);
/**
* Adds a message context into an operation context. Depending on MEPs, this method has to be
* overridden. Depending on the MEP operation description know how to fill the message context
* map in operationContext. As an example, if the MEP is IN-OUT then depending on messagable
* operation description should know how to keep them in correct locations.
*
* @param msgContext <code>MessageContext</code>
* @param opContext <code>OperationContext</code>
* @throws AxisFault <code>AxisFault</code>
*/
public abstract void addMessageContext(MessageContext msgContext, OperationContext opContext)
throws AxisFault;
public abstract void addFaultMessageContext(MessageContext msgContext,
OperationContext opContext)
throws AxisFault;
public void addModule(String moduleName) {
modulerefs.add(moduleName);
}
/**
* Adds module configuration, if there is moduleConfig tag in operation.
*
* @param moduleConfiguration a ModuleConfiguration which will be added (by name)
*/
public void addModuleConfig(ModuleConfiguration moduleConfiguration) {
moduleConfigmap.put(moduleConfiguration.getModuleName(), moduleConfiguration);
}
/**
* This is called when a module is engaged on this operation. Handle operation-specific tasks.
*
* @param axisModule AxisModule being engaged
* @param engager the AxisDescription where the engage occurred - could be us or a parent
* @throws AxisFault
*/
public final void onEngage(AxisModule axisModule, AxisDescription engager) throws AxisFault {
// Am I the source of this engagement?
boolean selfEngaged = (engager == this);
// If I'm not, the operations will already have been added by someone above, so don't
// do it again.
if (selfEngaged) {
AxisService service = getAxisService();
if (service != null) {
service.addModuleOperations(axisModule);
}
}
AxisConfiguration axisConfig = getAxisConfiguration();
PhaseResolver phaseResolver = new PhaseResolver(axisConfig);
phaseResolver.engageModuleToOperation(this, axisModule);
}
protected void onDisengage(AxisModule module) {
AxisService service = getAxisService();
if (service == null) return;
AxisConfiguration axisConfiguration = service.getAxisConfiguration();
PhaseResolver phaseResolver = new PhaseResolver(axisConfiguration);
if (!service.isEngaged(module.getName()) &&
(axisConfiguration != null && !axisConfiguration.isEngaged(module.getName()))) {
phaseResolver.disengageModuleFromGlobalChains(module);
}
phaseResolver.disengageModuleFromOperationChain(module, this);
//removing operations added at the time of module engagemnt
HashMap<QName, AxisOperation> moduleOperations = module.getOperations();
if (moduleOperations != null) {
Iterator<AxisOperation> moduleOperations_itr = moduleOperations.values().iterator();
while (moduleOperations_itr.hasNext()) {
AxisOperation operation = (AxisOperation)moduleOperations_itr.next();
service.removeOperation(operation.getName());
}
}
}
/**
* To remove module from engage module list
*
* @param module module to remove
* @deprecated please use disengageModule(), this method will disappear after 1.3
*/
public void removeFromEngagedModuleList(AxisModule module) {
try {
disengageModule(module);
} catch (AxisFault axisFault) {
// Can't do much here...
log.error(axisFault.getMessage(), axisFault);
}
}
// Note - removed this method which was dead code.
// private AxisOperation copyOperation(AxisOperation axisOperation) throws AxisFault {
/**
* Returns as existing OperationContext related to this message if one exists.
* <p/>
* TODO - why both this and findOperationContext()? (GD)
*
* @param msgContext the MessageContext for which we'd like an OperationContext
* @return the OperationContext, or null
* @throws AxisFault
*/
public OperationContext findForExistingOperationContext(MessageContext msgContext)
throws AxisFault {
OperationContext operationContext;
if ((operationContext = msgContext.getOperationContext()) != null) {
return operationContext;
}
// If this message is not related to another one, or it is but not one emitted
// from the same operation, don't further look for an operation context or fault.
if (null != msgContext.getRelatesTo()) {
// So this message may be part of an ongoing MEP
ConfigurationContext configContext = msgContext.getConfigurationContext();
operationContext =
configContext.getOperationContext(msgContext.getRelatesTo().getValue());
if (null == operationContext && log.isDebugEnabled()) {
log.debug(msgContext.getLogIDString() +
" Cannot correlate inbound message RelatesTo value [" +
msgContext.getRelatesTo() + "] to in-progree MEP");
}
}
return operationContext;
}
/**
* Finds an OperationContext for an incoming message. An incoming message can be of two states.
* <p/>
* 1)This is a new incoming message of a given MEP. 2)This message is a part of an MEP which has
* already begun.
* <p/>
* The method is special cased for the two MEPs
* <p/>
* #IN_ONLY #IN_OUT
* <p/>
* for two reasons. First reason is the wide usage and the second being that the need for the
* MEPContext to be saved for further incoming messages.
* <p/>
* In the event that MEP of this operation is different from the two MEPs defaulted above the
* decision of creating a new or this message relates to a MEP which already in business is
* decided by looking at the WSA Relates TO of the incoming message.
*
* @param msgContext MessageContext to search
* @param serviceContext ServiceContext (TODO - why pass this? (GD))
* @return the active OperationContext
*/
public OperationContext findOperationContext(MessageContext msgContext,
ServiceContext serviceContext)
throws AxisFault {
OperationContext operationContext;
if (null == msgContext.getRelatesTo()) {
// Its a new incoming message so get the factory to create a new
// one
operationContext = serviceContext.createOperationContext(this);
} else {
// So this message is part of an ongoing MEP
ConfigurationContext configContext = msgContext.getConfigurationContext();
operationContext =
configContext.getOperationContext(msgContext.getRelatesTo().getValue());
if (null == operationContext) {
throw new AxisFault(Messages.getMessage("cannotCorrelateMsg",
this.name.toString(),
msgContext.getRelatesTo().getValue()));
}
}
return operationContext;
}
public void registerOperationContext(MessageContext msgContext,
OperationContext operationContext)
throws AxisFault {
msgContext.setAxisOperation(this);
msgContext.getConfigurationContext().registerOperationContext(msgContext.getMessageID(),
operationContext);
operationContext.addMessageContext(msgContext);
msgContext.setOperationContext(operationContext);
if (operationContext.isComplete()) {
operationContext.cleanup();
}
}
public void registerMessageContext(MessageContext msgContext,
OperationContext operationContext) throws AxisFault {
msgContext.setAxisOperation(this);
operationContext.addMessageContext(msgContext);
msgContext.setOperationContext(operationContext);
if (operationContext.isComplete()) {
operationContext.cleanup();
}
}
/**
* Maps the String URI of the Message exchange pattern to an integer. Further, in the first
* lookup, it will cache the looked up value so that the subsequent method calls are extremely
* efficient.
*
* @return an MEP constant from WSDLConstants
*/
public int getAxisSpecificMEPConstant() {
if (this.mep != WSDLConstants.MEP_CONSTANT_INVALID) {
return this.mep;
}
int temp = WSDLConstants.MEP_CONSTANT_INVALID;
if (WSDL2Constants.MEP_URI_IN_OUT.equals(mepURI)) {
temp = WSDLConstants.MEP_CONSTANT_IN_OUT;
} else if (WSDL2Constants.MEP_URI_IN_ONLY.equals(mepURI)) {
temp = WSDLConstants.MEP_CONSTANT_IN_ONLY;
} else if (WSDL2Constants.MEP_URI_IN_OPTIONAL_OUT.equals(mepURI)) {
temp = WSDLConstants.MEP_CONSTANT_IN_OPTIONAL_OUT;
} else if (WSDL2Constants.MEP_URI_OUT_IN.equals(mepURI)) {
temp = WSDLConstants.MEP_CONSTANT_OUT_IN;
} else if (WSDL2Constants.MEP_URI_OUT_ONLY.equals(mepURI)) {
temp = WSDLConstants.MEP_CONSTANT_OUT_ONLY;
} else if (WSDL2Constants.MEP_URI_OUT_OPTIONAL_IN.equals(mepURI)) {
temp = WSDLConstants.MEP_CONSTANT_OUT_OPTIONAL_IN;
} else if (WSDL2Constants.MEP_URI_ROBUST_IN_ONLY.equals(mepURI)) {
temp = WSDLConstants.MEP_CONSTANT_ROBUST_IN_ONLY;
} else if (WSDL2Constants.MEP_URI_ROBUST_OUT_ONLY.equals(mepURI)) {
temp = WSDLConstants.MEP_CONSTANT_ROBUST_OUT_ONLY;
}
if (temp == WSDLConstants.MEP_CONSTANT_INVALID) {
throw new AxisError(Messages.getMessage("mepmappingerror"));
}
this.mep = temp;
return this.mep;
}
/*
* (non-Javadoc)
*
* @see org.apache.axis2.description.AxisService#getEngadgedModules()
*/
public abstract AxisMessage getMessage(String label);
public String getMessageExchangePattern() {
return mepURI;
}
public MessageReceiver getMessageReceiver() {
return messageReceiver;
}
public ModuleConfiguration getModuleConfig(String moduleName) {
return (ModuleConfiguration)moduleConfigmap.get(moduleName);
}
public ArrayList<String> getModuleRefs() {
return modulerefs;
}
public QName getName() {
return name;
}
public abstract ArrayList getPhasesInFaultFlow();
public abstract ArrayList getPhasesOutFaultFlow();
public abstract ArrayList getPhasesOutFlow();
public abstract ArrayList getRemainingPhasesInFlow();
public String getStyle() {
return style;
}
public ArrayList<String> getWSAMappingList() {
return wsamappingList;
}
public boolean isControlOperation() {
return controlOperation;
}
// to check whether a given parameter is locked
public boolean isParameterLocked(String parameterName) {
// checking the locked value of parent
boolean locked = false;
if (getParent() != null) {
locked = getParent().isParameterLocked(parameterName);
}
if (locked) {
return true;
} else {
Parameter parameter = getParameter(parameterName);
return (parameter != null) && parameter.isLocked();
}
}
public void setControlOperation(boolean controlOperation) {
this.controlOperation = controlOperation;
}
public void setMessageExchangePattern(String mepURI) {
this.mepURI = mepURI;
}
public void setMessageReceiver(MessageReceiver messageReceiver) {
this.messageReceiver = messageReceiver;
}
public void setName(QName name) {
this.name = name;
}
public abstract void setPhasesInFaultFlow(ArrayList list);
public abstract void setPhasesOutFaultFlow(ArrayList list);
public abstract void setPhasesOutFlow(ArrayList list);
public abstract void setRemainingPhasesInFlow(ArrayList list);
public void setStyle(String style) {
if (!"".equals(style)) {
this.style = style;
}
}
public void setWsamappingList(ArrayList<String> wsamappingList) {
this.wsamappingList = wsamappingList;
}
/**
* Return an OperationClient suitable for this AxisOperation.
*
* @param sc active ServiceContext
* @param options active Options
* @return an OperationClient set up appropriately for this operation
*/
public abstract OperationClient createClient(ServiceContext sc, Options options);
public Object getKey() {
return this.name;
}
public ArrayList<AxisMessage> getFaultMessages() {
return faultMessages;
}
public void setFaultMessages(AxisMessage faultMessage) {
faultMessage.setParent(this);
faultMessages.add(faultMessage);
if (getFaultAction(faultMessage.getName()) == null) {
addFaultAction(faultMessage.getName(),
"urn:" + name.getLocalPart() + faultMessage.getName());
}
}
public void setSoapAction(String soapAction) {
this.soapAction = soapAction;
}
/*
* Convenience method to access the WS-A Input Action per the
* WS-A spec. Effectively use the soapAction if available else
* use the first entry in the WSA Mapping list.
*
* Use getSoapAction when you want to get the soap action and this
* when you want to get the wsa input action.
*/
public String getInputAction() {
String result = null;
if (soapAction != null && !"".equals(soapAction)) {
result = soapAction;
} else {
if (wsamappingList != null && !wsamappingList.isEmpty()) {
result = (String)wsamappingList.get(0);
}
}
return result;
}
public String getOutputAction() {
return outputAction;
}
public void setOutputAction(String act) {
outputAction = act;
}
public void addFaultAction(String faultName, String action) {
faultActions.put(faultName, action);
}
public void removeFaultAction(String faultName) {
faultActions.remove(faultName);
}
public String getFaultAction(String faultName) {
return (String)faultActions.get(faultName);
}
public String[] getFaultActionNames() {
Set<String> keys = faultActions.keySet();
String[] faultActionNames = new String[keys.size()];
faultActionNames = (String[])keys.toArray(faultActionNames);
return faultActionNames;
}
public String getFaultAction() {
String result = null;
Iterator<String> iter = faultActions.values().iterator();
if (iter.hasNext()) {
result = (String)iter.next();
}
return result;
}
/**
* Get the messages referenced by this operation
*
* @return an Iterator of all the AxisMessages we deal with
*/
public Iterator<AxisMessage> getMessages() {
return (Iterator<AxisMessage>)getChildren();
}
/**
* Typesafe access to parent service
*
* @return the AxisService which contains this AxisOperation
*/
public AxisService getAxisService() {
return (AxisService)getParent();
}
public String getSoapAction() {
/*
* This AxisOperation instance may be used for the client OUT-IN or for
* the server IN-OUT. If the below code were changed to getInputActions, and the
* result of getInputAction were put in the SOAP action header on a client outbound
* message, the server would receive an INCORRECT SOAP action header. We should leave
* this as 'return soapAction;' OR make it client/server aware.
*/
return soapAction;
}
}