/* * 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.util.UIDGenerator; 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() + "_" + UIDGenerator.generateUID()); 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 = moduleOperations_itr.next(); service.removeOperation(operation.getName()); } } } // 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 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 wsamappingList) { if (log.isDebugEnabled()) { log.debug("setWsamappinglist"); } this.wsamappingList = wsamappingList; if (wsamappingList != null && !wsamappingList.isEmpty()) { if (log.isDebugEnabled()) { log.debug(" wsa action is " + (String)wsamappingList.get(0)); } } } /** * 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) { if(log.isDebugEnabled()) { log.debug("Entry: AxisOperation::setSoapAction, previous soapAction: " + this.soapAction + " updated soapAction: " + soapAction); } this.soapAction = soapAction; if(log.isDebugEnabled()) { log.debug("Exit: AxisOperation::setSoapAction"); } } /* * 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() { if(log.isDebugEnabled()) { log.debug("Entry: AxisOperation::getInputAction"); } String result = null; if (soapAction != null && !"".equals(soapAction)) { if(log.isDebugEnabled()) { log.debug("Debug: AxisOperation::getInputAction - using soapAction"); // so we know which path was taken // log wsa map to see if it matches or is set if (wsamappingList != null && !wsamappingList.isEmpty()) { log.debug(" but WSA map indicates " + (String)wsamappingList.get(0)); } } result = soapAction; } else { if (wsamappingList != null && !wsamappingList.isEmpty()) { if(log.isDebugEnabled()) { log.debug("Debug: AxisOperation::getInputAction - using wsamappingList"); } result = wsamappingList.get(0); } } if(log.isDebugEnabled()) { log.debug("Exit: AxisOperation::getInputAction " + result); } 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 faultActions.get(faultName); } public String[] getFaultActionNames() { Set<String> keys = faultActions.keySet(); String[] faultActionNames = new String[keys.size()]; faultActionNames = keys.toArray(faultActionNames); return faultActionNames; } public String getFaultAction() { String result = null; Iterator<String> iter = faultActions.values().iterator(); if (iter.hasNext()) { result = 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() { if(log.isDebugEnabled()) { log.debug("AxisOperation::getSoapAction " + soapAction); } /* * 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; } }