/*=============================================================================*
* 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.ws.resource.sg.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.xml.namespace.QName;
import org.w3c.dom.Element;
import org.apache.muse.core.Persistence;
import org.apache.muse.core.Resource;
import org.apache.muse.core.ResourceManager;
import org.apache.muse.core.ResourceManagerListener;
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.resource.WsResource;
import org.apache.muse.ws.resource.basefaults.BaseFault;
import org.apache.muse.ws.resource.ext.faults.ResourceInitializationFault;
import org.apache.muse.ws.resource.impl.AbstractWsResourceCapability;
import org.apache.muse.ws.resource.lifetime.ScheduledTermination;
import org.apache.muse.ws.resource.lifetime.WsrlConstants;
import org.apache.muse.ws.resource.sg.Entry;
import org.apache.muse.ws.resource.sg.MembershipContentRule;
import org.apache.muse.ws.resource.sg.ServiceGroup;
import org.apache.muse.ws.resource.sg.ServiceGroupPersistence;
import org.apache.muse.ws.resource.sg.WssgConstants;
import org.apache.muse.ws.resource.sg.faults.AddRefusedFault;
import org.apache.muse.ws.addressing.soap.SoapFault;
/**
*
* SimpleServiceGroup is Muse's default implementation of the WS-RF ServiceGroup
* capability. It provides SOAP-level access to the entries it contains and
* lifecycle management for the Entry resources. Users that wish to make entry
* creation available to remote clients should combine this capability with
* the {@linkplain SimpleServiceGroupRegistration ServiceGroupRegistration capability}.
*
* @author Dan Jemiolo (danj)
*
*/
public class SimpleServiceGroup
extends AbstractWsResourceCapability implements ServiceGroup, ResourceManagerListener
{
//
// Used to look up all exception messages
//
private static Messages _MESSAGES =
MessagesFactory.get(SimpleServiceGroup.class);
private MembershipContentRule[] _contentRules = null;
private Map _entriesByMemberEPR = new HashMap();
private String _entryPath = null;
public WsResource addEntry(EndpointReference memberEPR,
Element content,
Date termination)
throws AddRefusedFault,
BaseFault
{
WsResource entry = createEntry(memberEPR, content, termination);
return addEntry(memberEPR, entry);
}
public WsResource addEntry(EndpointReference memberEPR, WsResource entry)
{
_entriesByMemberEPR.put(memberEPR, entry);
return entry;
}
protected WsResource createEntry(EndpointReference epr,
Element content,
Date termination)
throws AddRefusedFault,
ResourceInitializationFault,
BaseFault
{
//
// NOTE: this implementation ignores the provided content
// because it reads the members' content elements
// via WS-RP in order to ensure compliance.
//
if (epr == null)
throw new NullPointerException(_MESSAGES.get("NullMemberServiceEPR"));
WsResource sg = getWsResource();
//
// make sure this is a valid member of the group
//
if (!isMatch(epr))
throw new AddRefusedFault(_MESSAGES.get("ContentCreationFailed"));
ResourceManager manager = sg.getResourceManager();
String endpoint = getEntryContextPath();
WsResource resource = null;
try
{
resource = (WsResource)manager.createResource(endpoint);
}
catch (SoapFault error)
{
throw new ResourceInitializationFault(error);
}
//
// set the sg entry fields before initializing the entry resource
//
Entry entry = (Entry)resource.getCapability(WssgConstants.ENTRY_URI);
entry.setServiceGroup(sg);
entry.setMemberEPR(epr);
try
{
resource.initialize();
manager.addResource(resource.getEndpointReference(), resource);
}
catch (SoapFault error)
{
throw new ResourceInitializationFault(error);
}
//
// set termination time AFTER we initialize so that we can rely
// on the base Resource class' implementation of WS-RL (which
// tries to apply the change immediately, rather than storing it
// in a field and setting it during initialization)
//
if (resource.hasCapability(WsrlConstants.SCHEDULED_TERMINATION_URI))
{
ScheduledTermination wsrl =
(ScheduledTermination)resource.getCapability(WsrlConstants.SCHEDULED_TERMINATION_URI);
wsrl.setTerminationTime(termination);
}
return resource;
}
protected MembershipContentRule createMembershipContentRule()
{
return new SimpleMembershipContentRule(getResource().getEndpointReference());
}
protected MembershipContentRule createMembershipContentRule(Element xml)
{
return new SimpleMembershipContentRule(getResource().getEndpointReference(), xml);
}
/**
*
* This implementation returns an empty array (no content rules, no
* restrictions on membership).
*
*/
protected MembershipContentRule[] createMembershipContentRules()
{
return new MembershipContentRule[0];
}
public QName[] getContentElements()
{
MembershipContentRule[] rules = getMembershipContentRule();
Collection allContent = new ArrayList();
for (int n = 0; n < rules.length; ++n)
{
QName[] content = rules[n].getContentElements();
allContent.addAll(Arrays.asList(content));
}
QName[] array = new QName[allContent.size()];
return (QName[])allContent.toArray(array);
}
public WsResource[] getEntry()
{
Collection entryResources = _entriesByMemberEPR.values();
WsResource[] asArray = new WsResource[entryResources.size()];
return (WsResource[])entryResources.toArray(asArray);
}
public WsResource getEntry(EndpointReference memberEPR)
{
return (WsResource)_entriesByMemberEPR.get(memberEPR);
}
protected String getEntryContextPath()
{
return _entryPath;
}
protected Element[] getEntryElements()
{
//
// convert resource -> entry capability -> entry XML
//
WsResource[] entries = getEntry();
Element[] entryXML = new Element[entries.length];
for (int n = 0; n < entries.length; ++n)
{
Entry entryCap = (Entry)entries[n].getCapability(WssgConstants.ENTRY_URI);
entryXML[n] = entryCap.toXML();
}
return entryXML;
}
public MembershipContentRule[] getMembershipContentRule()
{
return _contentRules;
}
public Element[] getProperty(QName property)
throws BaseFault
{
//
// special case to convert entry resources into entry XML
//
if (property.equals(WssgConstants.ENTRY_QNAME))
return getEntryElements();
return super.getProperty(property);
}
public QName[] getPropertyNames()
{
return PROPERTIES;
}
public void initialize()
throws SoapFault
{
super.initialize();
_contentRules = createMembershipContentRules();
//
// make sure the Entry endpoint is exposed, since our entries
// are all first-class resources
//
WsResource resource = getWsResource();
ResourceManager manager = resource.getResourceManager();
_entryPath = manager.getResourceContextPath(Entry.class);
if (_entryPath == null)
throw new RuntimeException(_MESSAGES.get("NoEntryEndpoint"));
//
// set up persistence, if it's used
//
Persistence persistence = getPersistence();
if (persistence != null)
{
if (!ServiceGroupPersistence.class.isAssignableFrom(persistence.getClass()))
{
Object[] filler = { ServiceGroupPersistence.class, persistence.getClass() };
throw new RuntimeException(_MESSAGES.get("IncorrectPersistenceRoot", filler));
}
ServiceGroupPersistence sgP = (ServiceGroupPersistence)persistence;
sgP.setServiceGroup(resource);
}
//
// listen for all new resources being created so they can
// be added to the service group
//
manager.addListener(this);
}
public boolean isMatch(EndpointReference epr)
{
MembershipContentRule[] rules = getMembershipContentRule();
for (int n = 0; n < rules.length; ++n)
if (!rules[n].isMatch(epr))
return false;
return true;
}
public void removeEntry(WsResource entry)
{
if (entry == null)
throw new NullPointerException(_MESSAGES.get("NullEntry"));
//
// this is hack-ish - we have to loop through a hash table to find
// the right resource. don't have time to go back and refactor the
// way we store entries and make this more efficient - besides, it's
// more important the additions and lookups be fast
//
Iterator i = _entriesByMemberEPR.keySet().iterator();
EndpointReference memberEPR = null;
while (i.hasNext() && memberEPR == null)
{
EndpointReference nextEPR = (EndpointReference)i.next();
WsResource nextEntry = (WsResource)_entriesByMemberEPR.get(memberEPR);
if (entry == nextEntry)
memberEPR = nextEPR;
}
if (_entriesByMemberEPR.remove(memberEPR) == null)
throw new RuntimeException(_MESSAGES.get("EntryNotFound"));
//
// if we're using persistence, remove the record of the entry
//
ServiceGroupPersistence persistence = (ServiceGroupPersistence)getPersistence();
try
{
if (persistence != null)
persistence.resourceRemoved(entry.getEndpointReference());
}
catch (SoapFault fault)
{
LoggingUtils.logError(getLog(), fault);
}
}
public void resourceAdded(EndpointReference epr, Resource resource)
throws SoapFault
{
//
// don't add entries for wssg:Entry resources, or we'll
// create an infinite recursion
//
if (resource.hasCapability(WssgConstants.SERVICE_GROUP_URI) ||
resource.hasCapability(WssgConstants.ENTRY_URI))
return;
//
// don't add duplicate entries. this check was added because of
// the persistence layer, which may cause duplicate entries to
// be attempted when the SGEntry resources are re-loaded and then
// when the actual members are re-loaded
//
if (getEntry(epr) != null)
return;
//
// don't add resources that don't validate against our
// membership content rules
//
if (!isMatch(epr))
return;
Date termination = null;
if (resource.hasCapability(WsrlConstants.SCHEDULED_TERMINATION_URI))
{
ScheduledTermination wsrl =
(ScheduledTermination)resource.getCapability(WsrlConstants.SCHEDULED_TERMINATION_URI);
termination = wsrl.getTerminationTime();
}
WsResource entry = addEntry(epr, null, termination);
//
// if we're using persistence, create a record of the entry
//
ServiceGroupPersistence persistence = (ServiceGroupPersistence)getPersistence();
try
{
if (persistence != null)
persistence.resourceAdded(entry.getEndpointReference(), entry);
}
catch (SoapFault fault)
{
throw new AddRefusedFault(fault);
}
}
public void resourceRemoved(EndpointReference epr)
{
//
// if the SG is destroyed, it means we're being told about
// our own destruction - don't do anything
//
if (hasBeenShutdown())
getWsResource().getResourceManager().removeListener(this);
else
{
WsResource entry = getEntry(epr);
//
// if there was no value in the SG for the EPR, it was
// either a wssg:Entry resource (entries do not have
// entries themselves, or we'd have infinite recursion)
// or a member that was removed by a remote client before
// the member was destroyed
//
if (entry != null)
removeEntry(entry);
}
}
public void shutdown()
throws SoapFault
{
//
// if we're being shutdown, we have to shutdown our entry
// resources or they'll be orphaned
//
WsResource[] entries = getEntry();
for (int n = 0; n < entries.length; ++n)
entries[n].shutdown();
super.shutdown();
}
}