/*=============================================================================*
* 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.descriptor;
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.wsdl.Definition;
import javax.wsdl.Operation;
import javax.wsdl.PortType;
import javax.xml.namespace.QName;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.apache.muse.core.Capability;
import org.apache.muse.core.Environment;
import org.apache.muse.core.Resource;
import org.apache.muse.core.routing.MessageHandler;
import org.apache.muse.core.routing.ReflectionMessageHandler;
import org.apache.muse.core.routing.ResourceIdFactory;
import org.apache.muse.util.ReflectUtils;
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;
import org.apache.muse.ws.wsdl.WsdlUtils;
/**
*
* @author Dan Jemiolo (danj)
*
*/
public class SimpleResourceDescriptor implements ResourceDescriptor
{
//
// Used to lookup all exception messages
//
private static Messages _MESSAGES =
MessagesFactory.get(SimpleResourceDescriptor.class);
private static final String _REQUIRED_CAPABILITIES = "org.apache.muse.core.descriptor.ext.RequiredCapabilityImplementations";
private ResourceDefinition _definition = null;
protected Collection createCapabilityDefinitions(Element xml,
Environment env,
WsdlConfig wsdlConfig)
throws SoapFault
{
Document wsdlDoc = WsdlUtils.createWSDL(env, wsdlConfig.getWsdlPath(), true);
//
// read in list of operation names and the return value names
//
Map opsByAction = getWsdlOperations(wsdlDoc, wsdlConfig, env);
Element[] capXML = XmlUtils.getElements(xml, DescriptorConstants.CAPABILITY_QNAME);
Collection definitions = new ArrayList(capXML.length);
//
// load the (optional) properties file that will tell us if there are
// any capabilities that the underlying platform provides and does
// not want to be superceded
//
Map requiredClassesByURI = createRequiredClasses(env);
for (int n = 0; n < capXML.length; ++n)
{
CapabilityDescriptor cd = createCapabilityDescriptor();
cd.load(capXML[n], env);
CapabilityDefinition definition = cd.getCapabilityDefinition();
String uri = definition.getURI();
Class implClass = definition.getImplementationClass();
//
// make sure it's obvious to the user that they have to use a
// certain impl class if specified by the platform. we don't
// want people trying to debug their code when it's not even
// being called by Muse.
//
if (requiredClassesByURI.keySet().contains(uri))
{
String requiredClassName = (String)requiredClassesByURI.get(uri);
if (implClass != null)
{
Object[] filler = { uri, requiredClassName };
throw new RuntimeException(_MESSAGES.get("RequiredCapability", filler));
}
//
// load the required implementation class
//
implClass = ReflectUtils.getClass(requiredClassName, env.getClassLoader());
definition.setImplementationClass(implClass);
}
//
// create reflection-based message handlers, which could be
// overridden by the capability impl
//
Collection handlers = createMessageHandlers(wsdlDoc, opsByAction, implClass);
definition.setMessageHandlers(handlers);
definitions.add(definition);
}
if (!opsByAction.isEmpty())
{
Object[] filler = { wsdlConfig.getWsdlPath(), opsByAction.keySet() };
throw new RuntimeException(_MESSAGES.get("MissingOperationImplementations", filler));
}
return definitions;
}
protected CapabilityDescriptor createCapabilityDescriptor()
{
return new SimpleCapabilityDescriptor();
}
protected String createContextPath(Element xml)
{
QName qname = DescriptorConstants.CONTEXT_PATH_QNAME;
String path = XmlUtils.getElementText(xml, qname);
if (path == null)
throw new RuntimeException(_MESSAGES.get("NullContextPath"));
return path;
}
protected InitParamDescriptor createInitParamDescriptor()
{
return new SimpleInitParamDescriptor();
}
protected MessageHandler createMessageHandler(String actionURI,
QName operationName,
QName returnValueName)
{
return new ReflectionMessageHandler(actionURI, operationName, returnValueName);
}
protected Collection createMessageHandlers(Document wsdlDoc, Map opsByAction, Class theClass)
{
Map methodsByName = new HashMap();
Method[] methods = theClass.getMethods();
for (int n = 0; n < methods.length; ++n)
{
Class declaringClass = methods[n].getDeclaringClass();
if (declaringClass != Object.class &&
declaringClass != Capability.class)
methodsByName.put(methods[n].getName(), methods[n]);
}
Collection handlers = new ArrayList(opsByAction.size());
Iterator i = opsByAction.keySet().iterator();
while (i.hasNext())
{
String action = (String)i.next();
Operation next = (Operation)opsByAction.get(action);
QName inputName = WsdlUtils.getInputPartName(next);
QName outputName = WsdlUtils.getOutputPartName(next, wsdlDoc);
String localName = next.getName();
String methodName = getMethodName(localName);
Method method = (Method)methodsByName.get(methodName);
if (method != null)
{
MessageHandler handler = createMessageHandler(action, inputName, outputName);
handler.setMethod(method);
handlers.add(handler);
i.remove();
}
}
return handlers;
}
protected Map createRequiredClasses(Environment env)
{
ResourceBundle bundle = null;
Map requiredClassesByURI = new HashMap();
try
{
bundle = ResourceBundle.getBundle(_REQUIRED_CAPABILITIES, Locale.getDefault(), env.getClassLoader());
}
catch (MissingResourceException notFound)
{
//
// this is not really an error - the file is optional
//
return requiredClassesByURI;
}
//
// copy values to a map - worthwhile in case we later change the
// key or value type to something other than string
//
Enumeration keys = bundle.getKeys();
while (keys.hasMoreElements())
{
String next = (String)keys.nextElement();
requiredClassesByURI.put(next, bundle.getString(next));
}
return requiredClassesByURI;
}
protected Class createResourceClass(Element xml, Environment env)
{
QName qname = DescriptorConstants.JAVA_RESOURCE_QNAME;
String className = XmlUtils.getElementText(xml, qname);
if (className == null)
throw new RuntimeException(_MESSAGES.get("NullJavaResourceClass"));
ClassLoader loader = env.getClassLoader();
Class resourceClass = ReflectUtils.getClass(className, loader);
if (!Resource.class.isAssignableFrom(resourceClass))
{
Object[] filler = { className, Resource.class.getName() };
String message = _MESSAGES.get("IncorrectResourceRoot", filler);
throw new RuntimeException(message);
}
return resourceClass;
}
protected ResourceIdFactory createResourceIdFactory(Element xml,
Environment env)
{
QName qname = DescriptorConstants.JAVA_ID_FACTORY_QNAME;
String className = XmlUtils.getElementText(xml, qname);
if (className == null)
return null;
Class theClass = null;
ClassLoader loader = env.getClassLoader();
theClass = ReflectUtils.getClass(className, loader);
if (!ResourceIdFactory.class.isAssignableFrom(theClass))
{
Object[] filler = { className, ResourceIdFactory.class.getName() };
throw new RuntimeException(_MESSAGES.get("NotResourceIdFactory", filler));
}
return (ResourceIdFactory)ReflectUtils.newInstance(theClass);
}
protected boolean createUsingPersistence(Element xml)
{
//
// if there's a null/empty value, Boolean.valueOf() returns 'false'
//
String attr = xml.getAttribute(DescriptorConstants.USE_ROUTER_PERSISTENCE);
return Boolean.valueOf(attr).booleanValue();
}
protected WsdlConfig createWSDL(Element xml)
{
QName qname = DescriptorConstants.WSDL_QNAME;
Element wsdlXML = XmlUtils.getElement(xml, qname);
if (wsdlXML == null)
throw new RuntimeException(_MESSAGES.get("NullWSDLFile"));
qname = DescriptorConstants.WSDL_FILE_QNAME;
String path = XmlUtils.getElementText(wsdlXML, qname);
if (path == null)
throw new RuntimeException(_MESSAGES.get("NullWSDLLocation"));
//
// get the WSDL portType
//
// NOTE: at this point, we cannot guarantee that the portType is
// actually in the WSDL
//
qname = DescriptorConstants.WSDL_PORT_TYPE_QNAME;
QName portType = XmlUtils.getQNameFromChild(wsdlXML, qname);
if (portType == null)
throw new RuntimeException(_MESSAGES.get("NullWSDLPortType"));
WsdlConfig wsdl = new WsdlConfig();
wsdl.setWsdlPath(path);
wsdl.setWsdlPortType(portType);
return wsdl;
}
private String getMethodName(String actionName)
{
return Character.toLowerCase(actionName.charAt(0)) + actionName.substring(1);
}
public ResourceDefinition getResourceDefinition()
{
return _definition;
}
protected Map getWsdlOperations(Document wsdlDoc,
WsdlConfig wsdlConfig,
Environment env)
{
String wsdlPath = wsdlConfig.getWsdlPath();
String absolutePath = env.getRealDirectory().getAbsolutePath();
if (wsdlPath.charAt(0) != '/')
absolutePath += '/';
absolutePath += wsdlPath;
File wsdlFile = new File(absolutePath);
File wsdlDir = wsdlFile.getParentFile();
Definition def = WsdlUtils.createDefinition(wsdlDoc, wsdlDir);
//
// find the port type named in the descriptor and process
// all of the operations
//
QName portTypeName = wsdlConfig.getWsdlPortType();
PortType portType = def.getPortType(portTypeName);
if (portType == null)
{
Object[] filler = { absolutePath, portTypeName };
throw new RuntimeException(_MESSAGES.get("PortTypeNotFound", filler));
}
Collection ops = portType.getOperations();
Iterator i = ops.iterator();
Map opsByAction = new HashMap();
while (i.hasNext())
{
Operation next = (Operation)i.next();
String action = WsdlUtils.getAction(next);
opsByAction.put(action, next);
}
return opsByAction;
}
public void load(Element xml, Environment environment)
throws SoapFault
{
boolean isUsingPersistence = createUsingPersistence(xml);
String contextPath = createContextPath(xml);
WsdlConfig wsdl = createWSDL(xml);
ResourceIdFactory idFactory = createResourceIdFactory(xml, environment);
Class resourceClass = createResourceClass(xml, environment);
Collection capabilities = createCapabilityDefinitions(xml, environment, wsdl);
InitParamDescriptor paramDesc = createInitParamDescriptor();
paramDesc.load(xml);
Map parameters = paramDesc.getInitializationParameters();
_definition = new ResourceDefinition();
_definition.setEnvironment(environment);
_definition.setUsingPersistence(isUsingPersistence);
_definition.setContextPath(contextPath);
_definition.setWsdlConfig(wsdl);
_definition.setResourceIdFactory(idFactory);
_definition.setResourceClass(resourceClass);
_definition.setCapabilityDefinitions(capabilities);
_definition.setInitializationParameters(parameters);
}
}