/*=============================================================================*
* 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;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.w3c.dom.Element;
import org.apache.muse.core.routing.MessageHandler;
import org.apache.muse.util.LoggingUtils;
import org.apache.muse.util.messages.Messages;
import org.apache.muse.util.messages.MessagesFactory;
import org.apache.muse.ws.addressing.EndpointReference;
import org.apache.muse.ws.addressing.MessageHeaders;
import org.apache.muse.ws.addressing.soap.SoapFault;
import org.apache.muse.ws.addressing.soap.SoapUtils;
/**
*
* SimpleResource is Muse's default implementation of the core resource
* type component. It provides all the code needed to collect capabilities
* and delegate request to them, as well as a central place for them to
* find and contact each other.
*
* @author Dan Jemiolo (danj)
*
*/
public class SimpleResource implements Resource
{
//
// Used to lookup all exception messages
//
private static Messages _MESSAGES = MessagesFactory.get(SimpleResource.class);
//
// WS-A Action -> Capability that defines the operation for the action
//
private Map _capabilitiesByAction = new HashMap();
//
// Unique capability URI -> Capability
//
private Map _capabilitiesByURI = new LinkedHashMap();
private String _contextPath = null;
private Environment _environment = null;
//
// The EPR for this resource instance
//
private EndpointReference _epr = null;
private boolean _hasBeenInitialized = false;
private boolean _hasBeenShutdown = false;
private Logger _log = null;
private ResourceManager _manager = null;
//
// initialization parameters from the deployment descriptor
//
private Map _parameters = Collections.EMPTY_MAP;
private String _wsdlPath = null;
private QName _wsdlPortType = null;
public void addCapability(Capability capability)
{
if (capability == null)
throw new RuntimeException(_MESSAGES.get("NullCapability"));
//
// the capability inherits some basic components from the resource
//
capability.setResource(this);
capability.setLog(getLog());
capability.setEnvironment(getEnvironment());
String uri = capability.getCapabilityURI();
//
// make sure we don't already have this capability
//
if (_capabilitiesByURI.containsKey(uri))
{
Object[] filler = { getContextPath(), uri };
throw new RuntimeException(_MESSAGES.get("DuplicateCapabilityURI", filler));
}
_capabilitiesByURI.put(uri, capability);
Iterator i = capability.getActions().iterator();
while (i.hasNext())
{
String action = (String)i.next();
//
// make sure some other capability isn't already using this action
// URI for one of its operations
//
if (_capabilitiesByAction.containsKey(action))
{
Capability duplicate = (Capability)_capabilitiesByAction.get(action);
Object[] filler = { getContextPath(), action, duplicate.getCapabilityURI() };
throw new RuntimeException(_MESSAGES.get("DuplicateAction", filler));
}
_capabilitiesByAction.put(action, capability);
}
}
public final Capability getCapability(String capabilityURI)
{
return (Capability)_capabilitiesByURI.get(capabilityURI);
}
/**
*
* @return All of the WS-A Action URIs supported by this resource.
*
*/
protected Collection getCapabilityActions()
{
return Collections.unmodifiableSet(_capabilitiesByAction.keySet());
}
/**
*
* @param action
* A WS-A Action URI.
*
* @return The Capability object with the method that maps to the
* given WS-A Action URI.
*
*/
protected Capability getCapabilityForAction(String action)
{
return (Capability)_capabilitiesByAction.get(action);
}
/**
*
* @return A collection with all of the capabilities' URIs.
*
*/
public final Collection getCapabilityURIs()
{
return Collections.unmodifiableSet(_capabilitiesByURI.keySet());
}
public final String getContextPath()
{
return _contextPath;
}
public final EndpointReference getEndpointReference()
{
return _epr;
}
public final Environment getEnvironment()
{
return _environment;
}
public final String getInitializationParameter(String name)
{
return (String)getInitializationParameters().get(name);
}
public final Map getInitializationParameters()
{
return _parameters;
}
public final Logger getLog()
{
return _log;
}
public ResourceManager getResourceManager()
{
return _manager;
}
public final String getWsdlPath()
{
return _wsdlPath;
}
public final QName getWsdlPortType()
{
return _wsdlPortType;
}
public final boolean hasBeenInitialized()
{
return _hasBeenInitialized;
}
public final boolean hasBeenShutdown()
{
return _hasBeenShutdown;
}
public final boolean hasCapability(String capabilityURI)
{
return getCapability(capabilityURI) != null;
}
public void initialize()
throws SoapFault
{
if (getLog() == null)
throw new IllegalStateException(_MESSAGES.get("NoLogger"));
if (getEnvironment() == null)
throw new IllegalStateException(_MESSAGES.get("NoEnvironment"));
if (getResourceManager() == null)
throw new IllegalStateException(_MESSAGES.get("NoResourceManager"));
if (getContextPath() == null)
throw new IllegalStateException(_MESSAGES.get("NoContextPath"));
if (getWsdlPath() == null)
throw new IllegalStateException(_MESSAGES.get("NoWSDLPath"));
if (getWsdlPortType() == null)
throw new IllegalStateException(_MESSAGES.get("NoWSDLPortType"));
initializeCapabilities();
//
// we made it! log confirmation message
//
Object[] filler = { getContextPath() };
String message = _MESSAGES.get("ResourceInitialized", filler);
getLog().info(message);
_hasBeenInitialized = true;
}
/**
*
* This method can be overrided to provide additional capability
* initialization logic that applies generally to all capabilities.
* Capability-specific initialization logic should be provided by
* overriding Capability.initialize().
*
*/
protected void initializeCapabilities()
throws SoapFault
{
String contextPath = getContextPath();
Logger log = getLog();
Iterator i = getCapabilityURIs().iterator();
//
// first create and initialize() all capabilities
//
while (i.hasNext())
{
String nextURI = (String)i.next();
Capability next = getCapability(nextURI);
next.initialize();
Object[] filler = { contextPath, nextURI };
log.fine(_MESSAGES.get("CapabilityInitialized", filler));
}
i = getCapabilityURIs().iterator();
//
// then call initializeCompleted() for post-initialize tasks
//
while (i.hasNext())
{
String nextURI = (String)i.next();
Capability next = getCapability(nextURI);
next.initializeCompleted();
Object[] filler = { contextPath, nextURI };
log.fine(_MESSAGES.get("CapabilityInitializationComplete", filler));
}
}
public Element invoke(Element soapBody)
{
//
// get WS-A action, which maps to a Capability's operation
//
MessageHeaders wsa = getEnvironment().getAddressingContext();
String action = wsa.getAction();
//
// get the capability that owns this operation so we can
// delegate the request
//
Capability capability = getCapabilityForAction(action);
if (capability == null)
{
Object[] filler = { getContextPath(), action };
SoapFault fault = new SoapFault(_MESSAGES.get("ActionNotSupported", filler));
return fault.toXML();
}
MessageHandler handler = capability.getMessageHandler(action);
Method method = handler.getMethod();
Object[] parameters = null;
try
{
//
// translate from XML -> POJO. the reflection call will take
// care of casting the generic Object references to the actual
// method parameter types
//
parameters = handler.fromXML(soapBody);
//
// invoke operation and translate result to XML
//
Object result = method.invoke(capability, parameters);
return handler.toXML(result);
}
catch (Throwable error)
{
//
// the exception is the generic reflection error, and useless
// to the programmer - we want to report on the REAL cause
//
Throwable cause = error.getCause();
if (cause != null) // sanity check
error = cause;
Logger log = getLog();
//
// log the details of de-serialization and reflection
//
if (parameters != null)
LoggingUtils.logCall(log, method, parameters);
LoggingUtils.logError(log, error);
SoapFault response = SoapUtils.convertToFault(error);
return response.toXML();
}
}
public final void setContextPath(String contextPath)
{
if (contextPath == null)
throw new NullPointerException(_MESSAGES.get("NullContextPath"));
_contextPath = contextPath;
}
public final void setEndpointReference(EndpointReference epr)
{
if (_epr != null && hasBeenInitialized())
throw new RuntimeException(_MESSAGES.get("ExistingResourceEPR"));
_epr = epr;
}
public final void setEnvironment(Environment environment)
{
if (environment == null)
throw new NullPointerException(_MESSAGES.get("NullEnvironment"));
_environment = environment;
}
public final void setInitializationParameters(Map parameters)
{
if (parameters == null)
parameters = Collections.EMPTY_MAP;
_parameters = parameters;
}
public final void setLog(Logger log)
{
if (log == null)
throw new NullPointerException(_MESSAGES.get("NullLogger"));
_log = log;
}
public void setResourceManager(ResourceManager manager)
{
if (manager == null)
throw new NullPointerException(_MESSAGES.get("NullResourceManager"));
_manager = manager;
}
public final void setWsdlPath(String wsdlPath)
{
if (wsdlPath == null)
throw new NullPointerException(_MESSAGES.get("NullWsdlPath"));
_wsdlPath = wsdlPath;
}
public final void setWsdlPortType(QName wsdlPortType)
{
if (wsdlPortType == null)
throw new NullPointerException(_MESSAGES.get("NullWsdlPortType"));
_wsdlPortType = wsdlPortType;
}
/**
*
* {@inheritDoc}
* <br><br>
* This implementation double-checks to make sure the resource hasn't
* already been destroyed and then nulls-out all references to internal
* data structures (this will highlight bugs caused by stale references
* and prevent "undefined behavior").
*
*/
public synchronized void shutdown()
throws SoapFault
{
//
// error - we've already been here
//
if (hasBeenShutdown())
throw new SoapFault(_MESSAGES.get("ResourceAlreadyDestroyed"));
ResourceManager manager = getResourceManager();
//
// remove resource visibility
//
// sanity check: make sure the resource was put in manager. this may
// not be the case for internal resources
//
if (manager.getResource(_epr) != null)
manager.removeResource(_epr);
//
// sanity check: nullify fields to make sure resource isn't being
// used afterwards. if it IS being used, the code will throw an NPE
// instead of performing undefined behavior.
//
_epr = null;
_environment = null;
//
// log confirmation message, then drop the log reference
//
_log.info(_MESSAGES.get("ResourceDestroyed", new Object[]{ _contextPath }));
_contextPath = null;
_log = null;
_hasBeenShutdown = true;
}
/**
*
* @return The resource's EPR, in string form.
*
*/
public String toString()
{
return getEndpointReference().toString();
}
}