/*=============================================================================*
* Copyright 2006 The Apache Software Foundation
*
* Licensed 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:
//
// 1. there is no child element, so we hope there is a simple (text)
// value in the root element
//
// OR
//
// 2. the parameter type is array, so we treat all child elements
// as members of the array
//
if (parameters.length == 1 &&
(elements.length == 0 || parameters[0].isArray()))
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();
//
// void methods -> empty element
//
if (returnType == Void.TYPE)
return XmlUtils.createElement(getResponseName());
//
// 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);
QName returnValueName = getReturnValueName();
QName responseBodyName = getResponseName();
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 complex types, we need a child element under the
// response body element
//
Element responseXML = XmlUtils.createElement(responseBodyName);
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;
}
}