/*
* 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.muse.core.routing;
import java.lang.reflect.Method;
import javax.xml.namespace.QName;
import org.w3c.dom.Element;
import org.apache.muse.core.serializer.Serializer;
import org.apache.muse.core.serializer.SerializerRegistry;
import org.apache.muse.util.messages.Messages;
import org.apache.muse.util.messages.MessagesFactory;
import org.apache.muse.util.xml.XmlUtils;
import org.apache.muse.ws.addressing.soap.SoapFault;
/**
*
* ReflectionMessageHandler is a generic implementation of
* {@linkplain org.apache.muse.core.routing.MessageHandler MessageHandler}
* that can be applied to any Java method. It uses reflection to determine
* what types are being used by a method signature and then invokes the
* {@linkplain Serializer Serializers} that have been registered with Muse
* to do the XML/POJO transformations. If a method references a type that is
* not included in Muse's set of built-in Serializers, you must register a
* Serializer for that type in order to take advantage of this class.
* <br><br>
* Muse uses this class by default if no MessageHandler is specified for
* a user-defined operation. Application initialization will fail if this
* default is specified but no Serializer is provided for a user-defined type.
* You can add custom MessageHandlers to your own capabilities programmatically
* using the Capability.setMessageHandlers() method.
*
* @author Dan Jemiolo (danj)
*
*/
public class ReflectionMessageHandler extends AbstractMessageHandler
{
//
// Used to lookup all exception messages
//
private static Messages _MESSAGES =
MessagesFactory.get(ReflectionMessageHandler.class);
private QName _returnValueName = null;
public ReflectionMessageHandler(String actionURI,
QName requestQName,
QName returnValueName)
{
super(actionURI, requestQName);
_returnValueName = returnValueName;
}
/**
*
* {@inheritDoc}
* <br><br>
* This implementation uses the MessageHandler's java.lang.reflect.Method
* to discover what parameter types can be expected in the given Element.
* It then looks up the Serializers and uses them to deserialize the child
* Elements in order.
* <br><br>
* If the Element has no child elements but has text content, <b>and</b>
* the method has one parameter, the given Element is parsed as though it
* were the parameter.
*
*/
public Object[] fromXML(Element xml)
throws SoapFault
{
Method method = getMethod();
if (method == null)
throw new IllegalStateException(_MESSAGES.get("NoMethod"));
if (xml == null)
return EMPTY_REQUEST;
Class[] parameters = method.getParameterTypes();
Object[] objects = new Object[parameters.length];
Element[] elements = XmlUtils.getAllElements(xml);
//
// special cases where the method only takes one parameter: if
// there is no child element, so we hope there is a simple (text)
// value in the root element
//
if (parameters.length == 1 && elements.length == 0)
elements = new Element[]{ xml };
//
// make sure we have the correct number of parameter values
//
if (elements.length != parameters.length)
{
Object[] filler = {
method.getName(), new Integer(parameters.length), new Integer(elements.length)
};
throw new SoapFault(_MESSAGES.get("IncorrectParams", filler));
}
SerializerRegistry registry = SerializerRegistry.getInstance();
//
// general case: apply appropriate serializer to each child element
//
for (int i = 0; i < elements.length; ++i)
{
Serializer ser = registry.getSerializer(parameters[i]);
objects[i] = ser.fromXML(elements[i]);
}
return objects;
}
protected QName getReturnValueName()
{
return _returnValueName;
}
/**
*
* {@inheritDoc}
* <br><br>
* This implementation uses the MessageHandler's java.lang.reflect.Method
* to discover what return type can be expected in the given Element. It
* then looks up the Serializer and uses it to serialize the object.
*
*/
public Element toXML(Object result)
throws SoapFault
{
Method method = getMethod();
if (method == null)
throw new IllegalStateException(_MESSAGES.get("NoMethod"));
Class returnType = method.getReturnType();
QName returnValueName = getReturnValueName();
QName responseBodyName = getResponseName();
//
// void methods & no output part = no soap body
//
if (returnType == Void.TYPE && responseBodyName == null)
return null;
Element responseXML = XmlUtils.createElement(responseBodyName);
//
// void method w/ output part = empty element
//
if (returnType == Void.TYPE)
return responseXML;
boolean isComplex = true;
//
// for many simple return types, we just embed the value in
// the body's response element
//
if (returnValueName == null)
{
isComplex = false;
returnValueName = responseBodyName;
}
//
// for all non-void methods, we need to find the serializer
// for the return type, then determine if it's an array and
// deal with it accordingly
//
SerializerRegistry registry = SerializerRegistry.getInstance();
Serializer ser = registry.getSerializer(returnType);
//
// for complex types, we need a child element under the
// response body element
//
Element valueXML = ser.toXML(result, returnValueName);
//
// for arrays, we put the children (array items) under the
// response body element
//
if (returnType.isArray())
XmlUtils.moveSubTree(valueXML, responseXML);
else if (isComplex)
responseXML.appendChild(valueXML);
else
responseXML = valueXML;
return responseXML;
}
}