001: /*
002: * Copyright 2001-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: /*
018: * Modified by Nabh Information Systems, Inc. for Stringbeans Web Services
019: * Framework.
020: *
021: * Modifications (c) 2005 Nabh Information Systems, Inc.
022: */
023:
024: package com.nabhinc.ws.soap;
025:
026: import org.apache.axis.AxisFault;
027: import org.apache.axis.Constants;
028: import org.apache.axis.MessageContext;
029: import org.apache.axis.components.logger.LogFactory;
030: import org.apache.axis.description.OperationDesc;
031: import org.apache.axis.description.ParameterDesc;
032: import org.apache.axis.description.ServiceDesc;
033: import org.apache.axis.constants.Style;
034: import org.apache.axis.handlers.soap.SOAPService;
035: import org.apache.axis.message.RPCElement;
036: import org.apache.axis.message.RPCHeaderParam;
037: import org.apache.axis.message.RPCParam;
038: import org.apache.axis.message.SOAPBodyElement;
039: import org.apache.axis.message.SOAPEnvelope;
040: import org.apache.axis.soap.SOAPConstants;
041: import org.apache.axis.utils.JavaUtils;
042: import org.apache.axis.utils.Messages;
043: import org.apache.commons.logging.Log;
044: import org.xml.sax.SAXException;
045:
046: import com.nabhinc.ws.server.InterceptorChain;
047: import com.nabhinc.ws.server.RequestInfo;
048:
049: import javax.xml.namespace.QName;
050: import javax.xml.rpc.holders.Holder;
051: import javax.wsdl.OperationType;
052: import java.lang.reflect.Method;
053: import java.util.ArrayList;
054: import java.util.Iterator;
055: import java.util.Vector;
056:
057: /**
058: * Implement message processing by walking over RPCElements of the
059: * envelope body, invoking the appropriate methods on the service object.
060: *
061: * @author Doug Davis (dug@us.ibm.com)
062: */
063: public class RPCProvider {
064: protected static Log log = LogFactory.getLog(RPCProvider.class
065: .getName());
066:
067: /**
068: * Process the current message.
069: * Result in resEnv.
070: *
071: * @param msgContext self-explanatory
072: * @param reqEnv the request envelope
073: * @param resEnv the response envelope
074: * @param obj the service object itself
075: */
076: public void processMessage(MessageContext msgContext,
077: SOAPEnvelope reqEnv, SOAPEnvelope resEnv,
078: RequestInfo reqInfo, InterceptorChain chain)
079: throws Throwable {
080: if (log.isDebugEnabled()) {
081: log.debug("Enter: RPCProvider.processMessage()");
082: }
083:
084: SOAPService service = msgContext.getService();
085: ServiceDesc serviceDesc = service.getServiceDescription();
086: OperationDesc operation = msgContext.getOperation();
087:
088: Vector bodies = reqEnv.getBodyElements();
089: if (log.isDebugEnabled()) {
090: log.debug(Messages.getMessage("bodyElems00", ""
091: + bodies.size()));
092: if (bodies.size() > 0) {
093: log.debug(Messages.getMessage("bodyIs00", ""
094: + bodies.get(0)));
095: }
096: }
097:
098: RPCElement body = null;
099:
100: // Find the first "root" body element, which is the RPC call.
101: for (int bNum = 0; body == null && bNum < bodies.size(); bNum++) {
102: // If this is a regular old SOAPBodyElement, and it's a root,
103: // we're probably a non-wrapped doc/lit service. In this case,
104: // we deserialize the element, and create an RPCElement "wrapper"
105: // around it which points to the correct method.
106: // FIXME : There should be a cleaner way to do this...
107: if (!(bodies.get(bNum) instanceof RPCElement)) {
108: SOAPBodyElement bodyEl = (SOAPBodyElement) bodies
109: .get(bNum);
110: // igors: better check if bodyEl.getID() != null
111: // to make sure this loop does not step on SOAP-ENC objects
112: // that follow the parameters! FIXME?
113: if (bodyEl.isRoot() && operation != null
114: && bodyEl.getID() == null) {
115: ParameterDesc param = operation.getParameter(bNum);
116: // at least do not step on non-existent parameters!
117: if (param != null) {
118: Object val = bodyEl.getValueAsType(param
119: .getTypeQName());
120: body = new RPCElement("", operation.getName(),
121: new Object[] { val });
122: }
123: }
124: } else {
125: body = (RPCElement) bodies.get(bNum);
126: }
127: }
128:
129: // special case code for a document style operation with no
130: // arguments (which is a strange thing to have, but whatever)
131: if (body == null) {
132: // throw an error if this isn't a document style service
133: if (!(serviceDesc.getStyle().equals(Style.DOCUMENT))) {
134: throw new Exception(Messages.getMessage("noBody00"));
135: }
136:
137: // look for a method in the service that has no arguments,
138: // use the first one we find.
139: ArrayList ops = serviceDesc.getOperations();
140: for (Iterator iterator = ops.iterator(); iterator.hasNext();) {
141: OperationDesc desc = (OperationDesc) iterator.next();
142: if (desc.getNumInParams() == 0) {
143: // found one with no parameters, use it
144: msgContext.setOperation(desc);
145: // create an empty element
146: body = new RPCElement(desc.getName());
147: // stop looking
148: break;
149: }
150: }
151:
152: // If we still didn't find anything, report no body error.
153: if (body == null) {
154: throw new Exception(Messages.getMessage("noBody00"));
155: }
156: }
157:
158: reqInfo.methodName = body.getMethodName();
159: Vector args = null;
160: try {
161: args = body.getParams();
162: } catch (SAXException e) {
163: if (e.getException() != null)
164: throw e.getException();
165: throw e;
166: }
167: int numArgs = args.size();
168:
169: // This may have changed, so get it again...
170: // FIXME (there should be a cleaner way to do this)
171: operation = msgContext.getOperation();
172:
173: if (operation == null) {
174: QName qname = new QName(body.getNamespaceURI(), body
175: .getName());
176: operation = serviceDesc.getOperationByElementQName(qname);
177:
178: if (operation == null) {
179: SOAPConstants soapConstants = msgContext == null ? SOAPConstants.SOAP11_CONSTANTS
180: : msgContext.getSOAPConstants();
181: if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) {
182: AxisFault fault = new AxisFault(
183: Constants.FAULT_SOAP12_SENDER, Messages
184: .getMessage("noSuchOperation",
185: reqInfo.methodName), null,
186: null);
187: fault
188: .addFaultSubCode(Constants.FAULT_SUBCODE_PROC_NOT_PRESENT);
189: throw new SAXException(fault);
190: } else {
191: throw new AxisFault(Constants.FAULT_CLIENT,
192: Messages.getMessage("noSuchOperation",
193: reqInfo.methodName), null, null);
194: }
195: } else {
196: msgContext.setOperation(operation);
197: }
198: }
199:
200: // Create the array we'll use to hold the actual parameter
201: // values. We know how big to make it from the metadata.
202: Object[] argValues = new Object[operation.getNumParams()];
203: // A place to keep track of the out params (INOUTs and OUTs)
204: ArrayList outs = new ArrayList();
205:
206: // Put the values contained in the RPCParams into an array
207: // suitable for passing to java.lang.reflect.Method.invoke()
208: // Make sure we respect parameter ordering if we know about it
209: // from metadata, and handle whatever conversions are necessary
210: // (values -> Holders, etc)
211: for (int i = 0; i < numArgs; i++) {
212: RPCParam rpcParam = (RPCParam) args.get(i);
213: Object value = rpcParam.getObjectValue();
214:
215: // first check the type on the paramter
216: ParameterDesc paramDesc = rpcParam.getParamDesc();
217:
218: // if we found some type info try to make sure the value type is
219: // correct. For instance, if we deserialized a xsd:dateTime in
220: // to a Calendar and the service takes a Date, we need to convert
221: if (paramDesc != null && paramDesc.getJavaType() != null) {
222:
223: // Get the type in the signature (java type or its holder)
224: Class sigType = paramDesc.getJavaType();
225:
226: // Convert the value into the expected type in the signature
227: value = JavaUtils.convert(value, sigType);
228:
229: rpcParam.setObjectValue(value);
230: if (paramDesc.getMode() == ParameterDesc.INOUT) {
231: outs.add(rpcParam);
232: }
233: }
234:
235: // Put the value (possibly converted) in the argument array
236: // make sure to use the parameter order if we have it
237: if (paramDesc == null || paramDesc.getOrder() == -1) {
238: argValues[i] = value;
239: } else {
240: argValues[paramDesc.getOrder()] = value;
241: }
242:
243: if (log.isDebugEnabled()) {
244: log.debug(" "
245: + Messages.getMessage("value00", ""
246: + argValues[i]));
247: }
248: }
249:
250: // See if any subclasses want a crack at faulting on a bad operation
251: // FIXME : Does this make sense here???
252: //String allowedMethods = (String) service.getOption("allowedMethods");
253: //checkMethodName(msgContext, allowedMethods, operation.getName());
254:
255: // Now create any out holders we need to pass in
256: int count = numArgs;
257: for (int i = 0; i < argValues.length; i++) {
258:
259: // We are interested only in OUT/INOUT
260: ParameterDesc param = operation.getParameter(i);
261: if (param.getMode() == ParameterDesc.IN)
262: continue;
263:
264: Class holderClass = param.getJavaType();
265: if (holderClass != null
266: && Holder.class.isAssignableFrom(holderClass)) {
267: int index = count;
268: // Use the parameter order if specified or just stick them to the end.
269: if (param.getOrder() != -1) {
270: index = param.getOrder();
271: } else {
272: count++;
273: }
274: // If it's already filled, don't muck with it
275: if (argValues[index] != null) {
276: continue;
277: }
278: argValues[index] = holderClass.newInstance();
279: // Store an RPCParam in the outs collection so we
280: // have an easy and consistent way to write these
281: // back to the client below
282: RPCParam p = new RPCParam(param.getQName(),
283: argValues[index]);
284: p.setParamDesc(param);
285: outs.add(p);
286: } else {
287: throw new AxisFault(Messages.getMessage(
288: "badOutParameter00", "" + param.getQName(),
289: operation.getName()));
290: }
291: }
292:
293: // OK! Now we can invoke the method
294: reqInfo.arguments = argValues;
295: reqInfo.method = operation.getMethod();
296:
297: // Object obj = reqInfo.serviceInfo.webService;
298: try {
299: chain.doIntercept(reqInfo);
300: if (reqInfo.redirect != null)
301: return;
302: /*
303: reqInfo.result = invokeMethod(msgContext,
304: operation.getMethod(),
305: obj, argValues);
306: */
307: //if (reqInfo.error != null) throw reqInfo.error;
308: } catch (IllegalArgumentException e) {
309: String methodSig = operation.getMethod().toString();
310: String argClasses = "";
311: for (int i = 0; i < argValues.length; i++) {
312: if (argValues[i] == null) {
313: argClasses += "null";
314: } else {
315: argClasses += argValues[i].getClass().getName();
316: }
317: if (i + 1 < argValues.length) {
318: argClasses += ",";
319: }
320: }
321: log.info(Messages.getMessage("dispatchIAE00", new String[] {
322: methodSig, argClasses }), e);
323: throw new AxisFault(Messages.getMessage("dispatchIAE00",
324: new String[] { methodSig, argClasses }), e);
325: }
326:
327: /** If this is a one-way operation, there is nothing more to do.
328: */
329: if (OperationType.ONE_WAY.equals(operation.getMep()))
330: return;
331:
332: /* Now put the result in the result SOAPEnvelope */
333: /*************************************************/
334: RPCElement resBody = new RPCElement(reqInfo.methodName
335: + "Response");
336: resBody.setPrefix(body.getPrefix());
337: resBody.setNamespaceURI(body.getNamespaceURI());
338: resBody.setEncodingStyle(msgContext.getEncodingStyle());
339:
340: try {
341: // Return first
342: if (operation.getMethod().getReturnType() != Void.TYPE) {
343: QName returnQName = operation.getReturnQName();
344: if (returnQName == null) {
345: String nsp = body.getNamespaceURI();
346: if (nsp == null || nsp.length() == 0) {
347: nsp = serviceDesc.getDefaultNamespace();
348: }
349: returnQName = new QName(msgContext.isEncoded() ? ""
350: : nsp, reqInfo.methodName + "Return");
351: }
352:
353: RPCParam param = new RPCParam(returnQName,
354: reqInfo.result);
355: param.setParamDesc(operation.getReturnParamDesc());
356:
357: if (!operation.isReturnHeader()) {
358: // For SOAP 1.2 rpc style, add a result
359: if (msgContext.getSOAPConstants() == SOAPConstants.SOAP12_CONSTANTS
360: && (serviceDesc.getStyle()
361: .equals(Style.RPC))) {
362: RPCParam resultParam = new RPCParam(
363: Constants.QNAME_RPC_RESULT, returnQName);
364: resultParam.setXSITypeGeneration(Boolean.FALSE);
365: resBody.addParam(resultParam);
366: }
367: resBody.addParam(param);
368: } else {
369: resEnv.addHeader(new RPCHeaderParam(param));
370: }
371:
372: }
373:
374: // Then any other out params
375: if (!outs.isEmpty()) {
376: for (Iterator i = outs.iterator(); i.hasNext();) {
377: // We know this has a holder, so just unwrap the value
378: RPCParam param = (RPCParam) i.next();
379: Holder holder = (Holder) param.getObjectValue();
380: Object value = JavaUtils.getHolderValue(holder);
381: ParameterDesc paramDesc = param.getParamDesc();
382:
383: param.setObjectValue(value);
384: if (paramDesc != null && paramDesc.isOutHeader()) {
385: resEnv.addHeader(new RPCHeaderParam(param));
386: } else {
387: resBody.addParam(param);
388: }
389: }
390: }
391: } catch (Exception e) {
392: throw e;
393: }
394:
395: resEnv.addBodyElement(resBody);
396: }
397:
398: /**
399: * This method encapsulates the method invocation.
400: * @param msgContext MessageContext
401: * @param method the target method.
402: * @param obj the target object
403: * @param argValues the method arguments
404: */
405: protected Object invokeMethod(MessageContext msgContext,
406: Method method, Object obj, Object[] argValues)
407: throws Exception {
408: return (method.invoke(obj, argValues));
409: }
410:
411: /**
412: * Throw an AxisFault if the requested method is not allowed.
413: * @param msgContext MessageContext
414: * @param allowedMethods list of allowed methods
415: * @param methodName name of target method
416: */
417: protected void checkMethodName(MessageContext msgContext,
418: String allowedMethods, String methodName) throws Exception {
419: // Our version doesn't need to do anything, though inherited
420: // ones might.
421: }
422: }
|