/******************************************************************************* * Copyright (c) 2009 MATERNA Information & Communications. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html. For further * project-related information visit http://www.ws4d.org. The most recent * version of the JMEDS framework can be obtained from * http://sourceforge.net/projects/ws4d-javame. ******************************************************************************/ package org.ws4d.java.service; import java.util.NoSuchElementException; import org.ws4d.java.constants.SOAPConstants; import org.ws4d.java.schema.ComplexType; import org.ws4d.java.schema.Element; import org.ws4d.java.schema.ElementContainer; import org.ws4d.java.schema.Type; import org.ws4d.java.service.parameter.ParameterValue; import org.ws4d.java.structures.DataStructure; import org.ws4d.java.structures.EmptyStructures; import org.ws4d.java.structures.HashMap; import org.ws4d.java.structures.HashSet; import org.ws4d.java.structures.Iterator; import org.ws4d.java.structures.ReadOnlyIterator; import org.ws4d.java.structures.Set; import org.ws4d.java.types.Attributable; import org.ws4d.java.types.AttributableSupport; import org.ws4d.java.types.CustomAttributeValue; import org.ws4d.java.types.QName; import org.ws4d.java.types.URI; import org.ws4d.java.util.StringUtil; import org.ws4d.java.util.WS4DIllegalStateException; import org.ws4d.java.wsdl.IOType; import org.ws4d.java.wsdl.WSDLMessagePart; import org.ws4d.java.wsdl.WSDLOperation; /** * This class is the common base for both {@link Operation}s and * {@link DefaultEventSource}s within the DPWS framework. It contains all common * properties for WSDL 1.1 compliant operations. It is further used to declare * an operation's or event's {@link #getInput() input} and {@link #getOuput() * output} parameters as well as any {@link #getFaults() faults} that may occur * on invocation. Overall in this class' documentation, the term * <em>"operation"</em> is used to mean either an actual * {@link Operation operation} or an {@link DefaultEventSource event}. * <p> * <strong>Note:</strong> According to <a href="http://www.w3.org/TR/wsdl">WSDL * 1.1 Specification</a>, an operation's {@ink #getName() name} is not required * to be unique within the scope of its containing port type in order to support * overloading. However, when overloading operations, the combination of each * one's {@link #getName() name}, {@link #getInputName() input name} and * {@link #getOutputName() output name} must be unique in order to avoid name * clashes. * </p> */ public abstract class OperationCommons extends AttributableSupport implements OperationDescription { /* The Transmission Type of the Operation */ protected int type = WSDLOperation.TYPE_UNKNOWN; protected Attributable inputAttributable; protected Attributable outputAttributable; /* The Name of this operation. */ private final String name; /* The WSDL PortType of the Operation */ private final QName portType; // Changed // 2010-08-11 // SSch // Changed // to // final, // Thx // to // Stefan // Schlichting private Service service; /* The name of the WSDL input element */ private String inputName; /* The name of the WSDL output element */ private String outputName; /* The wsa:Action input name. */ private String inputAction; /* The wsa:Action output name. */ private String outputAction; // key = local name of fault as String, value = Fault instance private final HashMap faults = new HashMap(); private Set customComplexTypes = null; private boolean managedInput = true; private boolean managedOutput = true; private Element input = null; private Element output = null; private boolean inputNameSet = false; private boolean outputNameSet = false; private boolean inputActionSet = false; private boolean outputActionSet = false; private boolean inputActionExtended = false; private boolean outputActionExtended = false; /** * default constructor * * Son Han added to solve the serialization problem * @date 2013/12/12 * */ protected OperationCommons() { super(); this.name = StringUtil.simpleClassName(this.getClass()); this.portType = new QName(StringUtil.simpleClassName(this.getClass()), null); } /** * Creates a new instance with the given name and port type. * * @param name the name of the operation, must be unique within the scope of * its port type * @param portType the qualified port type */ protected OperationCommons(String name, QName portType) { super(); if (name == null) { // this.name = this.getClass().getSimpleName(); this.name = StringUtil.simpleClassName(this.getClass()); } else { this.name = name; } if (portType == null) { // this.portType = new QName(this.getClass().getSimpleName(), null); this.portType = new QName(StringUtil.simpleClassName(this.getClass()), null); } else { this.portType = portType; } } protected OperationCommons(WSDLOperation operation) { this(operation.getName(), operation.getPortType().getName()); setType(operation.getType()); setInputName(operation.getInputName()); setOutputName(operation.getOutputName()); // copy action URIs from WSDLOperation to our operation setInputAction(operation.getInputAction()); setOutputAction(operation.getOutputAction()); if (operation.hasAttributes()) { setAttributes(operation.getAttributes()); } IOType input = operation.getInput(); if (input != null) { if (input.hasAttributes()) { setInputAttributes(input.getAttributes()); } } IOType output = operation.getOutput(); if (output != null) { if (output.hasAttributes()) { setOutputAttributes(output.getAttributes()); } } DataStructure inputParts = operation.getInputParts(); int size = inputParts.size(); if (size > 1) { throw new IllegalArgumentException("Unsupported WSDL feature (input message with more than one parts ): " + operation.getInputMessage()); } else if (size == 1) { WSDLMessagePart part = (WSDLMessagePart) inputParts.iterator().next(); if (part.isElement()) { setInput(part.getElement()); } else { /* * don't throw unsupported WSDL feature exception, if we can * create a unique element from the referenced type */ // throw new // IllegalArgumentException("Unsupported WSDL feature (message part referring to a type): " // + part); Element element = new Element(getInputName(), getPortType().getNamespace(), part.getType()); setInput(element); } } DataStructure outputParts = operation.getOutputParts(); size = outputParts.size(); if (size > 1) { throw new IllegalArgumentException("Unsupported WSDL feature (output message with more than one parts): " + operation.getInputMessage()); } else if (size == 1) { WSDLMessagePart part = (WSDLMessagePart) outputParts.iterator().next(); if (part.isElement()) { setOutput(part.getElement()); } else { /* * don't throw unsupported WSDL feature exception, if we can * create a unique element from the referenced type */ // throw new // IllegalArgumentException("Unsupported WSDL feature (message part referring to a type): " // + part); Element element = new Element(getOutputName(), getPortType().getNamespace(), part.getType()); setInput(element); } } // add faults, too DataStructure faults = operation.getFaults(); for (Iterator it = faults.iterator(); it.hasNext();) { IOType faultIO = (IOType) it.next(); Fault fault = new Fault(faultIO); if (faultIO.hasAttributes()) { fault.setAttributes(faultIO.getAttributes()); } addFault(fault); } } /* * (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { StringBuffer sb = new StringBuffer(getClass().getName()); sb.append(" [ name=").append(getName()); sb.append(", portType=").append(getPortType()); sb.append(", type=").append(WSDLOperation.typeToString(getType())); sb.append(", inputName=").append(getInputName()); sb.append(", outputName=").append(getOutputName()); sb.append(", inputAction=").append(getInputAction()); sb.append(", outputAction=").append(getOutputAction()); sb.append(", input=").append(getInput()); sb.append(", output=").append(getOutput()); sb.append(" ]"); return sb.toString(); } /* * (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ public final boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } OperationCommons other = (OperationCommons) obj; if (!name.equals(other.name)) { return false; } if (portType == null) { if (other.portType != null) { return false; } } else if (!portType.equals(other.portType)) { return false; } if (inputName == null) { if (other.inputName != null) { return false; } } else if (!inputName.equals(other.inputName)) { return false; } if (outputName == null) { if (other.outputName != null) { return false; } } else if (!outputName.equals(other.outputName)) { return false; } return true; } /* * (non-Javadoc) * @see java.lang.Object#hashCode() */ public final int hashCode() { final int prime = 31; int result = 1; result = prime * result + name.hashCode(); result = prime * result + ((portType == null) ? 0 : portType.hashCode()); result = prime * result + ((inputName == null) ? 0 : inputName.hashCode()); result = prime * result + ((outputName == null) ? 0 : outputName.hashCode()); return result; } /** * Returns the local name of this operation. See {@link OperationCommons * note on overloading operations}. * * @return the local name of this operation */ public String getName() { return name; } /** * Returns the port type that this operation belongs to. * * @return this operation's port type */ public QName getPortType() { return portType; } /** * Returns the <code>transmission type</code> of this operation according to * <a href="http://www.w3.org/TR/wsdl">WSDL 1.1 specification</a>. The value * returned is one of {@link WSDLOperation#TYPE_ONE_WAY}, * {@link WSDLOperation#TYPE_REQUEST_RESPONSE}, * {@link WSDLOperation#TYPE_NOTIFICATION} and * {@link WSDLOperation#TYPE_SOLICIT_RESPONSE}. * * @return the transmission type of this operation */ public abstract int getType(); /** * Returns the name of this operation's input. If this operation doesn't * have any input (i.e. its {@link #getType() transmission type} is * {@link WSDLOperation#TYPE_NOTIFICATION}, <code>null</code> is returned. * Otherwise, if no input name was previously {@link #setInputName(String) * set}, a default input name is generated according to WSDL 1.1 * Specification. * * @return this operation's input name * @see #setInputName(String) */ public String getInputName() { if (getType() == WSDLOperation.TYPE_NOTIFICATION) { return null; } if (inputName == null) { inputName = generateDefaultInputName(); inputNameSet = false; } return inputName; } /** * Sets the name of this operation's input. See {@link OperationCommons note * on overloading operations}. * * @param inputName the new input name to set * @throws WS4DIllegalStateException if this instance is already added to a * service * @see #getInputName() */ public void setInputName(String inputName) { checkModifiable(); setInputNameInternal(inputName); if (inputName != null) { inputNameSet = true; } } /** * Returns the WS-Addressing [action] property for the input message of this * operation. Will return <code>null</code> only if this operation's * {@link #getType() transmission type} is * {@link WSDLOperation#TYPE_NOTIFICATION}, i.e. the operation doesn't have * any input. Otherwise, if no input action was explicitly * {@link #setInputAction(String) set}, this method will generate a default * one according to the rules of <a * href="http://www.w3.org/TR/ws-addr-wsdl/">WS-Addressing 1.0 - WSDL * Binding</a>. * * @return the WS-Addressing [action] property of this operation's input * @see #setInputAction(String) */ public String getInputAction() { if (getType() == WSDLOperation.TYPE_NOTIFICATION) { return null; } if (inputAction == null) { inputAction = generateDefaultAction(true); inputActionSet = false; inputActionExtended = false; } return inputAction; } /** * Sets the WS-Addressing [action] property for this operation's input * message * * @param inputAction the new [action] to set; must be an absolute URI * @throws WS4DIllegalStateException if this instance is already added to a * service * @see #getInputAction() */ public void setInputAction(String inputAction) { checkModifiable(); this.inputAction = inputAction; if (inputAction != null) { inputActionSet = true; inputActionExtended = false; } } /** * Returns the name of this operation's output. If this operation doesn't * have any output (i.e. its {@link #getType() transmission type} is * {@link WSDLOperation#TYPE_ONE_WAY}, <code>null</code> is returned. * Otherwise, if no output name was previously * {@link #setOutputName(String) set} a default output name is generated * according to WSDL 1.1 Specification. * * @return this operation's output name * @see #setOutputName(String) */ public String getOutputName() { if (getType() == WSDLOperation.TYPE_ONE_WAY) { return null; } if (outputName == null) { outputName = generateDefaultOutputName(); outputNameSet = false; } return outputName; } /** * Sets the name of this operation's output. See {@link OperationCommons * note on overloading operations}. * * @param outputName the new output name to set * @throws WS4DIllegalStateException if this instance is already added to a * service * @see #getOutputName() */ public void setOutputName(String outputName) { checkModifiable(); setOutputNameInternal(outputName); if (outputName != null) { outputNameSet = true; } } /** * Returns the WS-Addressing [action] property for the output message of * this operation. Will return <code>null</code> only if this operation's * {@link #getType() transmission type} is * {@link WSDLOperation#TYPE_ONE_WAY}, i.e. the operation doesn't have any * output. Otherwise, if no output action is explicitly * {@link #setInputAction(String) set}, this method will generate a default * one according to the rules of <a * href="http://www.w3.org/TR/ws-addr-wsdl/">WS-Addressing 1.0 - WSDL * Binding</a>. * * @return the WS-Addressing [action] property of this operation's output * @see #setOutputAction(String) */ public String getOutputAction() { if (getType() == WSDLOperation.TYPE_ONE_WAY) { return null; } if (outputAction == null) { outputAction = generateDefaultAction(false); outputActionSet = false; outputActionExtended = false; } return outputAction; } /** * Sets the WS-Addressing [action] property for this operation's output * message. * * @param outputAction the new [action] to set; must be an absolute URI * @throws WS4DIllegalStateException if this instance has already been added * to a service * @see #getOutputAction() */ public void setOutputAction(String outputAction) { checkModifiable(); this.outputAction = outputAction; if (outputAction != null) { outputActionSet = true; outputActionExtended = false; } } /** * Returns the input element of this operation. The input element defines * the data structure for the input parameters of this operation. It can be * used to {@link #createInputValue() create} a suitable * {@link ParameterValue} container for the actual values when sending * messages to the operation. * * @return the input element * @see #setInput(Element) */ public Element getInput() { return input; } /** * TODO * * @param parameterName * @return */ public Element getInputParameter(String parameterName) { if (!managedInput) { throw new WS4DIllegalStateException("unable to retrieve input parameter when input is set explicitly via setInput(Element)"); } if (input == null) { return null; } ComplexType wrapperType = (ComplexType) input.getType(); ElementContainer wrapperContainer = wrapperType.getContainer(); return wrapperContainer.getLocalElementByName(new QName(parameterName, getPortType().getNamespace())); } /** * Sets the input of this operation. This element is assumed to contain all * input parameters of this operation. * * @param element the input element * @see #getInput() */ public void setInput(Element element) { checkModifiable(); checkNamespace(element); input = element; managedInput = false; resetType(); } /** * Adds an input parameter with the given <code>parameterName</code> and of * the specified <code>type</code> to this operation. This method creates a * wrapper element of a {@link ComplexType complex type} with a single * top-most container of type {@link ComplexType#CONTAINER_SEQUENCE} and * uses this as {@link #getInput() input} of the operation. The wrapper * element's name is equal to this operation's {@link #getName() name}. This * conforms to the <a href= * "http://atmanes.blogspot.com/2005/03/wrapped-documentliteral-convention.html" * >document-literal wrapped style binding</a> for SOAP 1.2. The new * parameter is then appended to the end of the sequence container of the * wrapper element. * <p> * <strong>NOTE:</strong> Using this method is only possible when the input * element for this operation was <em>NOT</em> previously set by a call to * {@link #setInput(Element)}. Otherwise, this method will throw a * {@link WS4DIllegalStateException}. * </p> * * @param parameterName the name of the input parameter to add * @param type the type for the new input parameter * @throws WS4DIllegalStateException if an input element was previously * {@link #setInput(Element) set} on this operation */ public void addInputParameter(String parameterName, Type type) { checkModifiable(); if (!managedInput) { throw new WS4DIllegalStateException("unable to add input parameter when input is set explicitly via setInput(Element)"); } ComplexType wrapperType; if (input == null) { wrapperType = new ComplexType(ComplexType.CONTAINER_SEQUENCE); input = new Element(new QName(getName(), getPortType().getNamespace()), wrapperType); resetType(); } else { wrapperType = (ComplexType) input.getType(); } ElementContainer wrapperContainer = wrapperType.getContainer(); wrapperContainer.addElement(new Element(new QName(parameterName, getPortType().getNamespace()), type)); managedInput = true; } /** * Returns the output element of this operation. The output element defines * the data structure for the output parameters of this operation. It can be * used to {@link #createOutputValue() create} a suitable * {@link ParameterValue} container for the actual values an operation * returns. * * @return the output element * @see #setOutput(Element) */ public Element getOutput() { return output; } /** * TODO * * @param parameterName * @return */ public Element getOutputParameter(String parameterName) { if (!managedOutput) { throw new WS4DIllegalStateException("unable to retrieve output parameter when output is set explicitly via setOutput(Element)"); } if (output == null) { return null; } ComplexType wrapperType = (ComplexType) output.getType(); ElementContainer wrapperContainer = wrapperType.getContainer(); return wrapperContainer.getLocalElementByName(new QName(parameterName, getPortType().getNamespace())); } /** * Sets the output of this operation. This element is assumed to contain all * output parameters of this operation. * * @param element the output element * @see #getOutput() */ public void setOutput(Element element) { checkModifiable(); checkNamespace(element); output = element; managedOutput = false; resetType(); } /** * Adds an output parameter with the given <code>parameterName</code> and of * the specified <code>type</code> to this operation. This method creates a * wrapper element of a {@link ComplexType complex type} with a single * top-most container of type {@link ComplexType#CONTAINER_SEQUENCE} and * uses this as {@link #getOutput() output} of the operation. The wrapper * element's name is equal to this operation's {@link #getName() name} with * a 'Response' suffix appended to it. This conforms to the <a href= * "http://atmanes.blogspot.com/2005/03/wrapped-documentliteral-convention.html" * >document-literal wrapped style binding</a> for SOAP 1.2. The new * parameter is then appended to the end of the sequence container of the * wrapper element. * <p> * <strong>NOTE:</strong> Using this method is only possible when the output * element for this operation was <em>NOT</em> previously set by a call to * {@link #setOutput(Element)}. Otherwise, this method will throw a * {@link WS4DIllegalStateException}. * </p> * * @param parameterName the name of the output parameter to add * @param type the type for the new output parameter * @throws WS4DIllegalStateException if an output element was previously * {@link #setOutput(Element) set} on this operation */ public void addOutputParameter(String parameterName, Type type) { checkModifiable(); if (!managedOutput) { throw new WS4DIllegalStateException("unable to add output parameter when output is set explicitly via setOutput(Element)"); } ComplexType wrapperType; if (output == null) { wrapperType = new ComplexType(ComplexType.CONTAINER_SEQUENCE); output = new Element(new QName(getName() + IOType.RESPONSE_SUFFIX, getPortType().getNamespace()), wrapperType); resetType(); } else { wrapperType = (ComplexType) output.getType(); } ElementContainer wrapperContainer = wrapperType.getContainer(); wrapperContainer.addElement(new Element(new QName(parameterName, getPortType().getNamespace()), type)); managedOutput = true; } /** * Returns an {@link Iterator} over all faults declared for this operation. * * @return an iterator over this operation's faults * @see #addFault(Fault) */ public Iterator getFaults() { return new ReadOnlyIterator(faults.values()); } /* * (non-Javadoc) * @see org.ws4d.java.service.OperationDescription#getFaultCount() */ public int getFaultCount() { return faults.size(); } /** * Returns this operation's fault with the given <code>faultName</code>, * which is considered to be unique within the operation's scope. * * @param faultName the requested fault's name * @return the fault with the given name or <code>null</code> if there is no * fault with that name within this operation * @see #addFault(Fault) * @see #getFaults() */ public Fault getFault(String faultName) { return (Fault) faults.get(faultName); } /** * Adds the given <code>fault</code> to this operation. * <p> * A fault on a web service operation corresponds to a checked declared * exception on a Java method. If an operation's * {@link Operation#invoke(ParameterValue) invocation} causes a fault, the * invoking client will receive an {@link InvocationException} providing * additional information about this fault. * </p> * * @param fault the fault to add * @throws NullPointerException if <code>fault</code> is null * @throws IllegalArgumentException if a fault with the given name already * exists, as fault names must be unique within the scope of an * operation * @see #getFaults() * @see #getFault(String) */ public void addFault(Fault fault) { checkModifiable(); if (fault == null) { throw new NullPointerException("fault is null"); } String faultName = fault.getName(); if (faults.containsKey(faultName)) { throw new IllegalArgumentException("duplicate fault name: " + faultName); } if (fault.getAction() == null) { // action not set explicitly, generate one fault.setAction(generateDefaultFaultAction(faultName)); } faults.put(fault.getName(), fault); fault.attached = true; checkNamespace(fault.getElement()); resetType(); } /** * Removes the fault with the given <code>faultName</code> from this * operation. * * @param faultName the name of the fault to remove * @see #addFault(Fault) * @see #getFault(String) */ public void removeFault(String faultName) { checkModifiable(); Fault fault = (Fault) faults.remove(faultName); if (fault != null) { fault.attached = false; } if (getFaultCount() == 0) { resetType(); } } /** * This is a shorthand method for creating a {@link ParameterValue parameter * value} container for the output of this operation. It is semantically * equivalent to * <code>ParameterValue.createElementValue(getOutput());</code>. * * @return a parameter container suitable for this operation's output * @see #getOutput() * @see ParameterValue#createElementValue(Element) */ public ParameterValue createOutputValue() { return ParameterValue.createElementValue(getOutput()); } /** * This is a shorthand method for creating a {@link ParameterValue parameter * value} container for the input of this operation. It is semantically * equivalent to <code>ParameterValue.createElementValue(getInput());</code> * . * * @return a parameter container suitable for this operation's input * @see #getInput() * @see ParameterValue#createElementValue(Element) */ public ParameterValue createInputValue() { return ParameterValue.createElementValue(getInput()); } /** * This method creates a {@link ParameterValue parameter value} container * for the fault with the given unique <code>faultName</code> (within the * scope of this operation). * * @param faultName the name of the fault to create a parameter container * for * @return the parameter container suitable for the specified fault * @throws NoSuchElementException if a fault with the given name is not * declared within this operation * @see #getFault(String) */ public ParameterValue createFaultValue(String faultName) { Fault fault = getFault(faultName); if (fault == null) { throw new NoSuchElementException("unknown fault: " + faultName); } return fault.createValue(); } /** * Returns the service to which this operation is associated. May return * <code>null</code>, if this operation has yet not been added to a * particular service. * * @return the service to which this operation belongs */ public Service getService() { return service; } /** * Returns the value of the input attribute with the given <code>name</code> * or <code>null</code> if this attribute is not available (or if its value * is explicitly set to <code>null</code>). * * @param attributeName the name of the input attribute of which to query * the value * @return the value for the named input attribute or <code>null</code> */ public CustomAttributeValue getInputAttribute(QName attributeName) { return inputAttributable == null ? null : inputAttributable.getAttribute(attributeName); } /** * Sets the <code>value</code> for the input attribute with the specified * <code>name</code>. Throws a * <code>java.lang.IllegalArgumentException</code> in case <code>name</code> * is <code>null</code>. * * @param attributeName the name of the input attribute to set, must not be * <code>null</code> * @param value the value to which to set the named input attribute (may be * <code>null</code> * @throws IllegalArgumentException if <code>name</code> is * <code>null</code> */ public void setInputAttribute(QName attributeName, CustomAttributeValue value) { if (inputAttributable == null) { inputAttributable = new AttributableSupport(); } inputAttributable.setAttribute(attributeName, value); } /** * Returns all input attributes explicitly set on this instance. Note that * depending on the actual implementation the returned reference may point * at the 'life map', i .e. the actual storage for the input attributes. * Modifications to that map should therefore be performed with care and * keeping this in mind. * * @return all already set input attributes */ public HashMap getInputAttributes() { if (inputAttributable == null) { inputAttributable = new AttributableSupport(); } return inputAttributable.getAttributes(); } /** * Sets all input attributes at once to those contained within argument * <code>attributes</code>. Note that depending on the actual implementation * it is possible that the map <code>attributes</code> points at may be used * for the actual internal storage of the input attributes (i.e. without * copying it). This is why, after passing it to this method, modifications * to this map should be made with care . This method throws a * <code>java.lang.IllegalArgumentException</code> in case * <code>attributes</code> is <code>null</code>. * * @param attributes the new input attributes to set * @throws IllegalArgumentException if <code>attributes</code> is * <code>null</code> */ public void setInputAttributes(HashMap attributes) { if (inputAttributable == null) { inputAttributable = new AttributableSupport(); } inputAttributable.setAttributes(attributes); } /** * Returns <code>true</code> only if this instance has at least one input * attribute set. Returns <code>false</code> in any other case. * * @return <code>true</code> only if there is at least one input attribute * set within this instance */ public boolean hasInputAttributes() { return inputAttributable != null && inputAttributable.hasAttributes(); } /** * Returns the value of the output attribute with the given * <code>name</code> or <code>null</code>, if this attribute is not * available (or if its value is actually explicitly set to * <code>null</code>). * * @param attributeName the name of the output attribute to query the value * of * @return the value for the named output attribute or <code>null</code> */ public CustomAttributeValue getOutputAttribute(QName attributeName) { return outputAttributable == null ? null : outputAttributable.getAttribute(attributeName); } /** * Sets the <code>value</code> of the output attribute with the specified * <code>name</code>. Throws a * <code>java.lang.IllegalArgumentException</code> in case <code>name</code> * is <code>null</code>. * * @param attributeName the name of the output attribute to set, must not be * <code>null</code> * @param value the value to set the named output attribute to (may be * <code>null</code> * @throws IllegalArgumentException if <code>name</code> is * <code>null</code> */ public void setOutputAttribute(QName attributeName, CustomAttributeValue value) { if (outputAttributable == null) { outputAttributable = new AttributableSupport(); } outputAttributable.setAttribute(attributeName, value); } /** * Returns all output attributes explicitly set on this instance. Note that * depending on the actual implementation the returned reference may point * at the 'life map', i .e. the actual storage for the output attributes. * Thus, modifications to that map should be performed with care and with * this in mind. * * @return all already set output attributes */ public HashMap getOutputAttributes() { if (outputAttributable == null) { outputAttributable = new AttributableSupport(); } return outputAttributable.getAttributes(); } /** * Sets all output attributes at once to those contained within argument * <code>attributes</code>. Note that depending on the actual implementation * it is possible that the map <code>attributes</code> points at may be used * for the actual internal storage of the output attributes (i.e. without * copying it). That is why modifications to this map should be made with * care after passing it to this method. This method throws a * <code>java.lang.IllegalArgumentException</code> in case * <code>attributes</code> is <code>null</code>. * * @param attributes the new output attributes to set * @throws IllegalArgumentException if <code>attributes</code> is * <code>null</code> */ public void setOutputAttributes(HashMap attributes) { if (outputAttributable == null) { outputAttributable = new AttributableSupport(); } outputAttributable.setAttributes(attributes); } /** * Returns <code>true</code> only if this instance has at least one output * attribute set. Returns <code>false</code> in any other case. * * @return <code>true</code> only if there is at least one output attribute * set within this instance */ public boolean hasOutputAttributes() { return outputAttributable != null && outputAttributable.hasAttributes(); } /** * Returns the value of the fault attribute with the given <code>name</code> * for the fault with the specified unique <code>faultName</code> or * <code>null</code>, either if this attribute is not available (or if its * value is actually explicitly set to <code>null</code>). * <p> * This method throws a <code>java.lang.IllegalArgumentException</code> if a * fault with the given <code>faultName</code> is not found within this * instance. * </p> * * @param faultName the unique name of the fault within the scope of this * instance, see {@link #getFault(String)} * @param attributeName the name of the fault attribute to query the value * of * @return the value for the named fault attribute or <code>null</code> * @throws IllegalArgumentException if no fault with the given * <code>faultName</code> is found */ public CustomAttributeValue getFaultAttribute(String faultName, QName attributeName) { Fault fault = getFault(faultName); if (fault == null) { throw new IllegalArgumentException("no such fault: " + faultName); } return fault.getAttribute(attributeName); } /** * Sets the <code>value</code> for the fault attribute with the specified * <code>name</code> of the fault with the given unique * <code>faultName</code>. Throws a * <code>java.lang.IllegalArgumentException</code> in case there is no fault * with the given <code>faultName</code> within this instance or if * <code>name</code> is <code>null</code>. * * @param faultName the unique name of the fault within the scope of this * instance, see {@link #getFault(String)} * @param attributeName the name of the fault attribute to set, must not be * <code>null</code> * @param value the value to set the named fault attribute to (may be * <code>null</code> * @throws IllegalArgumentException if there is no fault with the given * <code>faultName</code> within this instance or if * <code>name</code> is <code>null</code> */ public void setFaultAttribute(String faultName, QName attributeName, CustomAttributeValue value) { Fault fault = getFault(faultName); if (fault == null) { throw new IllegalArgumentException("no such fault: " + faultName); } fault.setAttribute(attributeName, value); } /** * Returns all fault attributes explicitly set on this instance for the * fault with the given unique <code>faultName</code>. Note that depending * on the actual implementation the returned reference may point at the * 'life map', i .e. the actual storage for the fault attributes. Thus, * modifications to that map should be performed with care and keeping this * in mind. * <p> * This method throws a <code>java.lang.IllegalArgumentException</code> if a * fault with the given <code>faultName</code> is not found within this * instance. * </p> * * @param faultName the unique name of the fault within the scope of this * instance, see {@link #getFault(String)} * @return all already set fault attributes * @throws IllegalArgumentException if no fault with the given * <code>faultName</code> is found */ public HashMap getFaultAttributes(String faultName) { Fault fault = getFault(faultName); if (fault == null) { throw new IllegalArgumentException("no such fault: " + faultName); } return fault.getAttributes(); } /** * Sets at once all fault attributes of the fault with unique * <code>faultName</code> to those contained within argument * <code>attributes</code>. Note that depending on the actual * implementation, it is possible that the map <code>attributes</code> * points at may be used for the actual internal storage of the fault * attributes (i.e. without copying it). That is why, after passing it to * this method, modifications to this map should be made with care. This * method throws a <code>java.lang.IllegalArgumentException</code> in case * <code>attributes</code> is <code>null</code>. * * @param faultName the unique name of the fault within the scope of this * instance, see {@link #getFault(String)} * @param attributes the new fault attributes to set * @throws IllegalArgumentException if no fault with the given * <code>faultName</code> is found or if <code>attributes</code> * is <code>null</code> */ public void setFaultAttributes(String faultName, HashMap attributes) { Fault fault = getFault(faultName); if (fault == null) { throw new IllegalArgumentException("no such fault: " + faultName); } fault.setAttributes(attributes); } /** * Returns <code>true</code> only if this instance has at least one fault * attribute set for the fault with the specified unique * <code>faultName</code>. Returns <code>false</code> in any other case, * including when there is no fault with the given <code>faultName</code>. * * @param faultName the unique name of the fault within the scope of this * instance, see {@link #getFault(String)} * @return <code>true</code> only if there is at least one fault attribute * set for the named fault within this instance */ public boolean hasFaultAttributes(String faultName) { Fault fault = getFault(faultName); return fault != null && fault.hasAttributes(); } /** * @param service the service to set */ public void setService(Service service) { this.service = service; } /** * This method doesn't toggle the inputNameSet flag * * @param inputName */ public void setInputNameInternal(String inputName) { this.inputName = inputName; if (!inputActionSet) { // reset action name, so we can generate default again inputAction = null; } } /** * This method doesn't toggle the outputNameSet flag * * @param outputName */ public void setOutputNameInternal(String outputName) { this.outputName = outputName; if (!outputActionSet) { // reset action name, so we can generate default again outputAction = null; } } public String setExtendedDefaultInputAction() { int type = getType(); if (type == WSDLOperation.TYPE_UNKNOWN) { return null; } String delim = IOType.URL_DELIMITER; if (portType.getNamespace().startsWith(URI.URN_SCHEMA_PREFIX)) { delim = IOType.URN_DELIMITER; } inputActionExtended = true; return inputAction = buildActionName(getInputName() + delim + getOutputName(), delim); } public String setExtendedDefaultOutputAction() { int type = getType(); if (type == WSDLOperation.TYPE_UNKNOWN) { return null; } String delim = IOType.URL_DELIMITER; if (portType.getNamespace().startsWith(URI.URN_SCHEMA_PREFIX)) { delim = IOType.URN_DELIMITER; } outputActionExtended = true; return outputAction = buildActionName(getOutputName() + delim + getInputName(), delim); } public boolean isInputNameSet() { return inputNameSet; } public boolean isInputActionSet() { return inputActionSet; } public boolean isOutputNameSet() { return outputNameSet; } public boolean isOutputActionSet() { return outputActionSet; } public boolean isInputActionExtended() { return inputActionExtended; } public boolean isOutputActionExtended() { return outputActionExtended; } /** * This method adds a custom <code>xsd:ComplexType</code> to the operation. * This custom type will be serialized in the WSDL document. * <p> * <h3>Notice</h3> Do <strong>not</strong> add custom types with the same * name as the types used for input or output definitions. This may * overwrite the normal definitions. * </p> * <p> * The added custom type <strong>MUST</strong> have a valid name. If no name * is set, this method will throw an {@link RuntimeException}. * </p> * * @param type the custom type which should be add to the WSDL document. */ public synchronized void addCustomComplexType(ComplexType type) { if (type.getName() == null) throw new RuntimeException("Cannot add custom complex type with out name to the operation."); if (customComplexTypes == null) { customComplexTypes = new HashSet(); } customComplexTypes.add(type); } /** * Remove a given custom <code>xsd:ComplexType</code> from the operation. * * @param type the custom type which should be removed. * @see #addCustomComplexType(ComplexType) */ public synchronized void removeCustomComplexType(ComplexType type) { if (customComplexTypes == null) { return; } customComplexTypes.remove(type); } /** * Clears the list of custom types. * * @see #addCustomComplexType(ComplexType) */ public synchronized void clearCustomComplexTypes() { if (customComplexTypes == null) { return; } customComplexTypes = null; } /** * Returns an iterator containing {@link ComplexType}. This list contains * the custom types set for this operation. * * @return an iterator containing {@link ComplexType}. * @see #addCustomComplexType(ComplexType) */ public synchronized Iterator getCustomComplexTypes() { if (customComplexTypes == null) { return EmptyStructures.EMPTY_ITERATOR; } return customComplexTypes.iterator(); } /** * Set the <code>type</code> of this operation, which is one of the * following: TYPE_UNKNOWN = -1; TYPE_ONE_WAY = 1; TYPE_REQUEST_RESPONSE = * 2; TYPE_SOLICIT_RESPONSE = 3; TYPE_NOTIFICATION = 4; * * @param type * @throws WS4DIllegalStateException if this instance has already been added * to a service */ private void setType(int type) { checkModifiable(); this.type = type; } private void checkModifiable() { if (service != null) { throw new WS4DIllegalStateException("unable to modify after being added to service"); } } private void checkNamespace(Element element) { if (element != null) { QName name = element.getName(); if (name != null) { String namespace = name.getNamespace(); String localPart = name.getLocalPart(); String prefix = name.getPrefix(); int priority = name.getPriority(); if ("".equals(namespace)) { if (portType == null) { namespace = generateDefaultNamespace(); } else { namespace = getPortType().getNamespace(); } name = new QName(localPart, namespace, prefix, priority); element.setName(name); element.checkNamespace(element.getType()); } } } } private String generateDefaultNamespace() { return "http://www.ws4d.org"; } private String generateDefaultInputName() { return generateDefaultName(true); } private String generateDefaultOutputName() { return generateDefaultName(false); } private String generateDefaultName(boolean input) { String opName = getName(); switch (getType()) { case (WSDLOperation.TYPE_ONE_WAY): { // input equal to this operation's name if (input) { return opName; } break; } case (WSDLOperation.TYPE_NOTIFICATION): { // output equal to this operation's name if (!input) { return opName; } break; } case (WSDLOperation.TYPE_REQUEST_RESPONSE): { // input equal to this operation's name + "Request" suffix if (input) { return opName + IOType.REQUEST_SUFFIX; } // output equal to this operation's name + "Response" suffix else if (!input) { return opName + IOType.RESPONSE_SUFFIX; } break; } case (WSDLOperation.TYPE_SOLICIT_RESPONSE): { // output equal to this operation's name + "Solicit" suffix if (!input) { return opName + IOType.SOLICIT_SUFFIX; } // input equal to this operation's name + "Response" suffix else if (input) { return opName + IOType.RESPONSE_SUFFIX; } break; } } return null; } /** * Decides and generates a DefaultActionName * * @param input * @return DefaultActionName */ private String generateDefaultAction(boolean input) { int type = getType(); if (type == WSDLOperation.TYPE_UNKNOWN) { return null; } String delim = IOType.URL_DELIMITER; if (portType.getNamespace().startsWith(URI.URN_SCHEMA_PREFIX)) { delim = IOType.URN_DELIMITER; } switch (type) { case (WSDLOperation.TYPE_ONE_WAY): { return buildActionName(getInputName(), delim); } case (WSDLOperation.TYPE_REQUEST_RESPONSE): { if (input) return buildActionName(getInputName(), delim); else return buildActionName(getOutputName(), delim); } case (WSDLOperation.TYPE_SOLICIT_RESPONSE): { if (input) return buildActionName(getInputName(), delim); else return buildActionName(getOutputName(), delim); } case (WSDLOperation.TYPE_NOTIFICATION): { return buildActionName(getOutputName(), delim); } } return null; } /** * Builds the DefaultActionName String * * @param delim * @param ioTypeName * @return DefaultActionName String */ private String buildActionName(String ioTypeName, String delim) { StringBuffer text = new StringBuffer(); String namespace = portType.getNamespace(); text.append(namespace); if (!namespace.endsWith(delim)) { text.append(delim); } text.append(portType.getLocalPart()); text.append(delim); text.append(ioTypeName); return text.toString(); } /** * Decides and generates a DefaultFaultAction String * * @param faultName * @return defaultFaultAction String */ private String generateDefaultFaultAction(String faultName) { String delim = IOType.URL_DELIMITER; String namespace = portType.getNamespace(); if (namespace.startsWith(URI.URN_SCHEMA_PREFIX)) { delim = IOType.URN_DELIMITER; } StringBuffer text = new StringBuffer(); text.append(namespace); if (!namespace.endsWith(delim)) { text.append(delim); } text.append(portType.getLocalPart()); text.append(delim); text.append(getName()); text.append(delim); text.append(SOAPConstants.SOAP_ELEM_FAULT); text.append(delim); text.append(faultName); return text.toString(); } private void resetType() { type = WSDLOperation.TYPE_UNKNOWN; } }