/*=============================================================================*
* 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.net.URI;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.muse.core.descriptor.CapabilityDefinition;
import org.apache.muse.core.descriptor.ResourceDefinition;
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.soap.SoapFault;
/**
*
* SimpleResourceManager is Muse's default implementation of resource lifecycle
* management and the implied resource pattern. It maintains a simple table
* that maps EPRs to Resource objects. Each EPR that is added to the manager
* must be unique, but multiple EPRs may be mapped to the same Resource object.
*
* @author Dan Jemiolo (danj)
*
*/
public class SimpleResourceManager implements ResourceManager
{
//
// Used to lookup all exception messages
//
private static Messages _MESSAGES =
MessagesFactory.get(SimpleResourceManager.class);
//
// context path -> ResourceDefinition - all the data needed to
// properly instantiate each resource type in muse.xml
//
private Map _definitionsByPath = new LinkedHashMap();
private Environment _environment = null;
private boolean _hasBeenInitialized = false;
private boolean _hasBeenShutdown = false;
//
// the ResourceManagerListeners, in the order they were added
//
private List _listeners = new LinkedList();
//
// All instances of a resource type currently alive on the system
//
private Map _resources = new HashMap();
public synchronized void addListener(ResourceManagerListener listener)
{
_listeners.add(listener);
}
public void addResource(EndpointReference epr, Resource resource)
throws SoapFault
{
if (resource == null)
throw new NullPointerException(_MESSAGES.get("NullResource"));
synchronized (this)
{
//
// the key already exists - not allowed!
//
if (_resources.containsKey(epr))
throw new SoapFault(_MESSAGES.get("ResourceEPRExists", new Object[]{ epr }));
_resources.put(epr, resource);
}
//
// tell all listeners that a new resource is now available
//
Iterator i = getListeners().iterator();
while (i.hasNext())
{
ResourceManagerListener next = (ResourceManagerListener)i.next();
next.resourceAdded(epr, resource);
}
}
public void addResourceDefinitions(Collection definitions)
{
if (definitions == null)
throw new NullPointerException(_MESSAGES.get("NullFactoryCollection"));
if (definitions.isEmpty())
throw new IllegalArgumentException(_MESSAGES.get("EmptyFactoryCollection"));
Iterator i = definitions.iterator();
while (i.hasNext())
{
ResourceDefinition definition = (ResourceDefinition)i.next();
String endpoint = definition.getContextPath();
//
// sanity check - don't allow one component to over-write another
//
if (_definitionsByPath.containsKey(endpoint))
{
Object[] filler = { endpoint };
throw new RuntimeException(_MESSAGES.get("ContextPathExists", filler));
}
_definitionsByPath.put(endpoint, definition);
}
}
public Resource createResource(String contextPath)
throws SoapFault
{
ResourceDefinition definition = getResourceDefinition(contextPath);
if (definition == null)
{
Object[] filler = { contextPath, _definitionsByPath.keySet() };
throw new SoapFault(_MESSAGES.get("ContextPathNotFound", filler));
}
Resource resource = definition.newInstance();
resource.setResourceManager(this);
fixContextPath(resource); // HACK: see javadoc for fixContextPath()
return resource;
}
/**
*
* HACK: This method addresses the fact that all new resources have
* EPRs with wsa:Addresses of the resource type being targeted
* by the current request. The Environment looks up the target
* EPR and uses that as the basis for new EPR. In cases where
* one resource instance is creating another internally, however,
* we want the new EPR to have a wsa:Address of the given
* resource type, not the requester's. This method will hack
* around that problem by adjusting the wsa:Address to have the
* proper endpoint value.
*
* @param resource
* A newly-created resource whose wsa:Address does not
* reflect its resource type.
*
*/
private void fixContextPath(Resource resource)
{
//
// get the current EPR/Address, and find the part where the
// context path starts...
//
EndpointReference epr = resource.getEndpointReference();
String address = epr.getAddress().toString();
int slash = address.lastIndexOf('/');
StringBuffer buffer = new StringBuffer();
buffer.append(address.substring(0, slash));
//
// substitute the resource type's context path for the one
// that was on there originally
//
String contextPath = resource.getContextPath();
if (contextPath.charAt(0) != '/')
buffer.append('/');
buffer.append(contextPath);
//
// re-set EPR address
//
URI addressWithProperEndpoint = URI.create(buffer.toString());
epr.setAddress(addressWithProperEndpoint);
}
public Environment getEnvironment()
{
return _environment;
}
/**
*
* @return A <em>copy</m> of the list of ResourceManagerListeners.
*
*/
protected List getListeners()
{
return new LinkedList(_listeners);
}
public synchronized int getNumberOfResources()
{
return _resources.size();
}
public synchronized Resource getResource(EndpointReference epr)
{
if (epr == null)
throw new NullPointerException(_MESSAGES.get("NullResourceEPR"));
//
// make sure that required property value is used during
// equality check of EPRs
//
String address = epr.getAddress().toString();
ResourceDefinition definition = getResourceDefinition(address);
if (definition == null)
return null;
return (Resource)_resources.get(epr);
}
public String getResourceContextPath(Class capabilityClass)
{
Collection matches = getResourceContextPaths(capabilityClass);
return matches.isEmpty() ? null : (String)matches.iterator().next();
}
public Collection getResourceContextPaths()
{
return _definitionsByPath.keySet();
}
public Collection getResourceContextPaths(Class capabilityClass)
{
if (capabilityClass == null)
throw new NullPointerException(_MESSAGES.get("NullClass"));
Iterator i = getResourceContextPaths().iterator();
Collection matches = new LinkedList();
//
// for all distinct resource types...
//
while (i.hasNext())
{
String path = (String)i.next();
ResourceDefinition resource = getResourceDefinition(path);
Iterator j = resource.getCapabilityDefinitions().iterator();
//
// read through the capability definitions and try to
// match the impl or interface class
//
while (j.hasNext())
{
CapabilityDefinition capability = (CapabilityDefinition)j.next();
Class next = capability.getImplementationClass();
if (capabilityClass.isAssignableFrom(next))
matches.add(resource.getContextPath());
}
}
return matches;
}
/**
*
* @param contextPath
* The URL targeted by an incoming request - this should map to
* a <em>context-path</em> value in muse.xml.
*
* @return The ResourceDefinition associated with the given endpoint,
* or null if there is no such type.
*
*/
protected ResourceDefinition getResourceDefinition(String contextPath)
{
if (contextPath == null)
throw new NullPointerException(_MESSAGES.get("NullContextPath"));
Iterator i = _definitionsByPath.keySet().iterator();
while (i.hasNext())
{
String next = (String)i.next();
//
// NOTE: we use endsWith() instead of equals() because we only
// care about the unique part of the URL - the part that
// applies to the resource type specifically
//
if (contextPath.endsWith(next))
return (ResourceDefinition)_definitionsByPath.get(next);
}
return null;
}
/**
*
* @return This implementation returns an iterator over a <b>copy</b> of the
* collection of resource EPRs.
*
*/
public synchronized Iterator getResourceEPRs()
{
Set copy = new HashSet(_resources.keySet());
return copy.iterator();
}
public boolean hasBeenInitialized()
{
return _hasBeenInitialized;
}
public boolean hasBeenShutdown()
{
return _hasBeenShutdown;
}
public void initialize()
throws SoapFault
{
_hasBeenInitialized = true;
}
public boolean isUsingPersistence(String contextPath)
{
return getResourceDefinition(contextPath).isUsingPersistence();
}
public synchronized void removeListener(ResourceManagerListener listener)
{
_listeners.remove(listener);
}
public void removeResource(EndpointReference epr)
throws SoapFault
{
//
// we use this lock instead of adding synchronized to the
// signature so that the listeners can access the ResourceManager
// through other threads (i.e., if a listener causes another
// remote request to come into this manager, the request will
// block because the manager is locked).
//
synchronized (this)
{
Resource resource = (Resource)_resources.remove(epr);
//
// can't remove things that don't exist
//
if (resource == null)
{
Object[] filler = { epr };
throw new SoapFault(_MESSAGES.get("ResourceEPRNotFound", filler));
}
}
//
// tell all listeners that the resource is gone forever
//
Iterator i = getListeners().iterator();
while (i.hasNext())
{
ResourceManagerListener next = (ResourceManagerListener)i.next();
next.resourceRemoved(epr);
}
}
public void removeResourceDefinitions(Collection definitions)
{
if (definitions == null)
throw new NullPointerException(_MESSAGES.get("NullFactoryCollection"));
Iterator i = definitions.iterator();
while (i.hasNext())
{
ResourceDefinition next = (ResourceDefinition)i.next();
String path = next.getContextPath();
_definitionsByPath.remove(path);
}
}
public void setEnvironment(Environment environment)
{
if (environment == null)
throw new NullPointerException(_MESSAGES.get("NullEnvironment"));
_environment = environment;
}
/**
*
* This implementation loops through all existing resource instances
* and tells them to shutdown().
*
*/
public void shutdown()
throws SoapFault
{
Iterator i = getResourceEPRs();
while (i.hasNext())
{
Resource next = getResource((EndpointReference)i.next());
next.shutdown();
}
_hasBeenShutdown = true;
}
}