package jadex.standalone.service;
import jadex.base.fipa.DFComponentDescription;
import jadex.base.fipa.DFServiceDescription;
import jadex.base.fipa.IDF;
import jadex.base.fipa.IDFComponentDescription;
import jadex.base.fipa.IDFServiceDescription;
import jadex.base.fipa.IProperty;
import jadex.base.fipa.SFipa;
import jadex.base.fipa.SearchConstraints;
import jadex.bridge.IComponentIdentifier;
import jadex.bridge.IComponentManagementService;
import jadex.bridge.ISearchConstraints;
import jadex.commons.Future;
import jadex.commons.IFuture;
import jadex.commons.collection.IndexMap;
import jadex.commons.concurrent.CollectionResultListener;
import jadex.commons.concurrent.IResultListener;
import jadex.commons.service.BasicService;
import jadex.commons.service.IServiceProvider;
import jadex.commons.service.SServiceProvider;
import jadex.commons.service.clock.IClockService;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Directory facilitator implementation for standalone platform.
*/
public class DirectoryFacilitatorService extends BasicService implements IDF
{
//-------- attributes --------
/** The platform. */
protected IServiceProvider provider;
/** The cached component management service. */
protected IComponentManagementService cms;
/** The cached clock service. */
protected IClockService clockservice;
/** The registered components. */
protected IndexMap components;
//-------- constructors --------
/**
* Create a standalone df.
*/
public DirectoryFacilitatorService(IServiceProvider provider)
{
this(provider, null);
}
/**
* Create a standalone df.
*/
public DirectoryFacilitatorService(IServiceProvider provider, Map properties)
{
super(provider.getId(), IDF.class, properties);
this.provider = provider;
this.components = new IndexMap();
}
//-------- IDF interface methods --------
/**
* Register a component description.
* @throws RuntimeException when the component description is already registered.
*/
public IFuture register(IDFComponentDescription cdesc)
{
Future ret = new Future();
//System.out.println("Registered: "+adesc.getName()+" "+adesc.getLeaseTime());
IDFComponentDescription clone = SFipa.cloneDFComponentDescription(cdesc, cms, this);
// Add description, when valid.
if(clone.getLeaseTime()==null || clone.getLeaseTime().getTime()>clockservice.getTime())
{
synchronized(components)
{
// Automatically throws exception, when key exists.
if(components.containsKey(clone.getName()))
throw new RuntimeException("Componentomponent already registered: "+cdesc.getName());
components.add(clone.getName(), clone);
// System.out.println("registered: "+clone.getName());
}
ret.setResult(clone);
}
else
{
ret.setException(new RuntimeException("Componentomponent not registered: "+clone.getName()));
// System.out.println("not registered: "+clone.getName());
}
return ret;
}
/**
* Deregister a component description.
* @throws RuntimeException when the component is not registered.
*/
public IFuture deregister(IDFComponentDescription cdesc)
{
Future ret = new Future();
synchronized(components)
{
if(!components.containsKey(cdesc.getName()))
{
//throw new RuntimeException("Component not registered: "+adesc.getName());
ret.setException(new RuntimeException("Component not registered: "+cdesc.getName()));
}
else
{
components.removeKey(cdesc.getName());
ret.setResult(null);
//System.out.println("deregistered: "+adesc.getName());
}
}
return ret;
}
/**
* Modify a component description.
* @throws RuntimeException when the component is not registered.
*/
public IFuture modify(IDFComponentDescription cdesc)
{
Future ret = new Future();
// Use clone to avoid caller manipulating object after insertion.
IDFComponentDescription clone = SFipa.cloneDFComponentDescription(cdesc, cms, this);
// Change description, when valid.
if(clone.getLeaseTime()==null || clone.getLeaseTime().getTime()>clockservice.getTime())
{
// Automatically throws exception, when key does not exist.
synchronized(components)
{
components.replace(clone.getName(), clone);
}
//System.out.println("modified: "+clone.getName());
ret.setResult(clone);
}
else
{
//throw new RuntimeException("Invalid lease time: "+clone.getLeaseTime());
ret.setException(new RuntimeException("Invalid lease time: "+clone.getLeaseTime()));
}
return ret;
}
/**
* Search for components matching the given description.
* @return An array of matching component descriptions.
*/
public IFuture search(final IDFComponentDescription adesc, final ISearchConstraints con)
{
return search(adesc, con, false);
}
// protected List open = Collections.synchronizedList(new ArrayList());
/**
* Search for components matching the given description.
* @return An array of matching component descriptions.
*/
public IFuture search(final IDFComponentDescription adesc, final ISearchConstraints con, boolean remote)
{
final Future fut = new Future();
//System.out.println("Searching: "+adesc.getName());
final List ret = new ArrayList();
// If name is supplied, just lookup description.
if(adesc.getName()!=null)
{
synchronized(components)
{
if(components.containsKey(adesc.getName()))
{
DFComponentDescription ad = (DFComponentDescription)components.get(adesc.getName());
// Remove description when invalid.
if(ad.getLeaseTime()!=null && ad.getLeaseTime().getTime()<clockservice.getTime())
components.removeKey(ad.getName());
else
ret.add(ad);
}
}
}
// Otherwise search for matching descriptions.
else
{
synchronized(components)
{
DFComponentDescription[] descs = (DFComponentDescription[])components.toArray(new DFComponentDescription[components.size()]);
for(int i=0; (con==null || con.getMaxResults()==-1 || ret.size()<con.getMaxResults()) && i<descs.length; i++)
{
// Remove description when invalid.
if(descs[i].getLeaseTime()!=null && descs[i].getLeaseTime().getTime()<clockservice.getTime())
{
components.removeKey(descs[i].getName());
}
// Otherwise match against template.
else
{
if(match(descs[i] ,adesc))
{
ret.add(descs[i]);
}
}
}
}
}
// System.out.println("Started search: "+ret);
// open.add(fut);
if(remote)
{
SServiceProvider.getServices(provider, IDF.class, true).addResultListener(new IResultListener()
{
public void resultAvailable(Object source, Object result)
{
Collection coll = (Collection)result;
// System.out.println("dfs: "+coll);
// Ignore search failures of remote dfs
CollectionResultListener lis = new CollectionResultListener(coll.size(), true, new IResultListener()
{
public void resultAvailable(Object source, Object result)
{
// Add all services of all remote dfs
for(Iterator it=((Collection)result).iterator(); it.hasNext(); )
{
IDFComponentDescription[] res = (IDFComponentDescription[])it.next();
if(res!=null)
{
for(int i=0; i<res.length; i++)
{
ret.add(res[i]);
}
}
}
// open.remove(fut);
// System.out.println("Federated search: "+ret);//+" "+open);
fut.setResult(ret.toArray(new DFComponentDescription[ret.size()]));
}
public void exceptionOccurred(Object source, Exception exception)
{
// open.remove(fut);
fut.setException(exception);
// fut.setResult(ret.toArray(new DFComponentDescription[ret.size()]));
}
});
for(Iterator it=coll.iterator(); it.hasNext(); )
{
IDF remotedf = (IDF)it.next();
if(remotedf!=DirectoryFacilitatorService.this)
{
remotedf.search(adesc, con, false).addResultListener(lis);
}
else
{
lis.resultAvailable(null, null);
}
}
}
public void exceptionOccurred(Object source, Exception exception)
{
// open.remove(fut);
fut.setResult(ret.toArray(new DFComponentDescription[ret.size()]));
}
});
}
else
{
// open.remove(fut);
// System.out.println("Local search: "+ret+" "+open);
fut.setResult(ret.toArray(new DFComponentDescription[ret.size()]));
}
//System.out.println("Searched: "+ret);
//return (ComponentDescription[])ret.toArray(new ComponentDescription[ret.size()]);
return fut;
}
/**
* Create a df service description.
* @param name The name.
* @param type The type.
* @param ownership The ownership.
* @return The service description.
*/
public IDFServiceDescription createDFServiceDescription(String name, String type, String ownership)
{
return new DFServiceDescription(name, type, ownership);
}
/**
* Create a df service description.
* @param name The name.
* @param type The type.
* @param ownership The ownership.
* @param languages The languages.
* @param ontologies The ontologies.
* @param protocols The protocols.
* @param properties The properties.
* @return The service description.
*/
public IDFServiceDescription createDFServiceDescription(String name, String type, String ownership,
String[] languages, String[] ontologies, String[] protocols, IProperty[] properties)
{
DFServiceDescription ret = new DFServiceDescription(name, type, ownership);
for(int i=0; languages!=null && i<languages.length; i++)
ret.addLanguage(languages[i]);
for(int i=0; ontologies!=null && i<ontologies.length; i++)
ret.addOntology(ontologies[i]);
for(int i=0; protocols!=null && i<protocols.length; i++)
ret.addProtocol(protocols[i]);
for(int i=0; properties!=null && i<properties.length; i++)
ret.addProperty(properties[i]);
return ret;
}
/**
* Create a df component description.
* @param component The component.
* @param service The service.
* @return The df component description.
*/
public IDFComponentDescription createDFComponentDescription(IComponentIdentifier component, IDFServiceDescription service)
{
DFComponentDescription ret = new DFComponentDescription();
ret.setName(component);
if(service!=null)
ret.addService(service);
return ret;
}
/**
* Create a new df component description.
* @param component The component id.
* @param services The services.
* @param languages The languages.
* @param ontologies The ontologies.
* @param protocols The protocols.
* @return The component description.
*/
public IDFComponentDescription createDFComponentDescription(IComponentIdentifier component, IDFServiceDescription[] services,
String[] languages, String[] ontologies, String[] protocols, Date leasetime)
{
DFComponentDescription ret = new DFComponentDescription();
ret.setName(component);
ret.setLeaseTime(leasetime);
for(int i=0; services!=null && i<services.length; i++)
ret.addService(services[i]);
for(int i=0; languages!=null && i<languages.length; i++)
ret.addLanguage(languages[i]);
for(int i=0; ontologies!=null && i<ontologies.length; i++)
ret.addOntology(ontologies[i]);
for(int i=0; protocols!=null && i<protocols.length; i++)
ret.addProtocol(protocols[i]);
return ret;
}
/**
* Create a search constraints object.
* @param maxresults The maximum number of results.
* @param maxdepth The maximal search depth.
* @return The search constraints.
*/
public ISearchConstraints createSearchConstraints(int maxresults, int maxdepth)
{
SearchConstraints ret = new SearchConstraints();
ret.setMaxResults(maxresults);
ret.setMaxDepth(maxdepth);
return ret;
}
/**
* Create a component identifier.
* @param name The name.
* @param local True for local name ().
* @return The new component identifier.
* /
public IComponentIdentifier createComponentIdentifier(String name, boolean local)
{
if(local)
name = name + "@" + platform.getName();
return new ComponentIdentifier(name);
}*/
/**
* Create a component identifier.
* @param name The name.
* @param local True for local name.
* @param addresses The addresses.
* /
public IComponentIdentifier createComponentIdentifier(String name, boolean local, String[] addresses)
{
if(local)
name = name + "@" + platform.getName();
return new ComponentIdentifier(name, addresses, null);
}*/
//-------- IPlatformService interface methods --------
/**
* Start the service.
*/
public synchronized IFuture startService()
{
final Future ret = new Future();
super.startService().addResultListener(new IResultListener()
{
public void resultAvailable(Object source, Object result)
{
final boolean[] services = new boolean[2];
SServiceProvider.getServiceUpwards(provider, IComponentManagementService.class).addResultListener(new IResultListener()
{
public void resultAvailable(Object source, Object result)
{
cms = (IComponentManagementService)result;
boolean setresult;
synchronized(services)
{
services[0] = true;
setresult = services[0] && services[1];
}
if(setresult)
ret.setResult(DirectoryFacilitatorService.this);
}
public void exceptionOccurred(Object source, Exception exception)
{
ret.setException(exception);
}
});
SServiceProvider.getService(provider, IClockService.class).addResultListener(new IResultListener()
{
public void resultAvailable(Object source, Object result)
{
clockservice = (IClockService)result;
boolean setresult;
synchronized(services)
{
services[1] = true;
setresult = services[0] && services[1];
}
if(setresult)
ret.setResult(DirectoryFacilitatorService.this);
}
public void exceptionOccurred(Object source, Exception exception)
{
ret.setException(exception);
}
});
}
public void exceptionOccurred(Object source, Exception exception)
{
ret.setException(exception);
}
});
return ret;
}
//-------- helper methods --------
/**
* Test if a component description matches a given template.
*/
protected boolean match(IDFComponentDescription desc, IDFComponentDescription template)
{
boolean ret = true;
// Match protocols, languages, and ontologies.
ret = includes(desc.getLanguages(), template.getLanguages());
ret = ret && includes(desc.getOntologies(), template.getOntologies());
ret = ret && includes(desc.getProtocols(), template.getProtocols());
// Match service descriptions.
if(ret)
{
IDFServiceDescription[] tservices = template.getServices();
for(int t=0; ret && t<tservices.length; t++)
{
ret = false;
IDFServiceDescription[] dservices = desc.getServices();
for(int d=0; !ret && d<dservices.length; d++)
{
ret = match(dservices[d], tservices[t]);
}
}
}
return ret;
}
/**
* Test if a service description matches a given template.
*/
protected boolean match(IDFServiceDescription desc, IDFServiceDescription template)
{
// Match name, type, and ownership;
boolean ret = template.getName()==null || template.getName().equals(desc.getName());
ret = ret && (template.getType()==null || template.getType().equals(desc.getType()));
ret = ret && (template.getOwnership()==null || template.getOwnership().equals(desc.getOwnership()));
// Match protocols, languages, ontologies, and properties.
ret = ret && includes(desc.getLanguages(), template.getLanguages());
ret = ret && includes(desc.getOntologies(), template.getOntologies());
ret = ret && includes(desc.getProtocols(), template.getProtocols());
ret = ret && includes(desc.getProperties(), template.getProperties());
return ret;
}
/**
* Test if one array of objects is included in the other
* (without considering the order).
* Test is performed using equals().
*/
protected boolean includes(Object[] a, Object[] b)
{
Set entries = new HashSet();
for(int i=0; i<b.length; i++)
entries.add(b[i]);
for(int i=0; i<a.length; i++)
entries.remove(a[i]);
return entries.isEmpty();
}
}