package org.gcube.common.clients.delegates; import static org.gcube.common.clients.cache.Key.*; import java.util.ArrayList; import java.util.List; import org.gcube.common.clients.Call; import org.gcube.common.clients.cache.EndpointCache; import org.gcube.common.clients.cache.Key; import org.gcube.common.clients.config.DiscoveryConfig; import org.gcube.common.clients.config.Property; import org.gcube.common.clients.exceptions.DiscoveryException; import org.gcube.common.clients.queries.Query; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A {@link ProxyDelegate} that discovers service endpoints. * *
*
* The delegates attempt to make {@link Call}s to endpoints cached in an {@link EndpointCache}.
* If the calls fail, or the cache is empty, they execute a {@link Query} for endpoints and call the results in turn until the call succeeds or there are no
* more endpoints to call. If the call succeeds with one endpoint, the delegates cache the endpoint in the {@link EndpointCache}.
*
* @author Fabio Simeoni
*
* @param the type of service addresses
* @param the type of service stubs
*
* @see Query
* @see EndpointCache
*/
public class DiscoveryDelegate extends AbstractDelegate> {
private static Logger log = LoggerFactory.getLogger(DiscoveryDelegate.class);
/**
* Creates an instance with a {@link ProxyPlugin}, a {@link Query}, and an {@link EndpointCache}.
*
* @param plugin the plugin
* @param query the query
* @param cache the cache
*/
public DiscoveryDelegate(DiscoveryConfig config) {
super(config);
}
//helper
private boolean isAnchored() {
return config().hasProperty(Property.sticky_session) ?
config().property(Property.sticky_session,Boolean.class):
false;
}
@Override
public call) throws Exception {
ProxyPlugin plugin = config().plugin();
Query query = config().query();
EndpointCache cache = config().cache();
// create key in call scope
Key key = key(plugin.name(),query);
// try with each endpoint in turn
Exception lastFault = null;
// use cache first, if any
A lgeAddress = cache.get(key);
use_cache:if (lgeAddress != null)
try {
log.info("calling {} @ {} (cached)", plugin.name(),lgeAddress);
S lge = null;
try {
lge=plugin.resolve(lgeAddress,config());
}
catch(Exception e) {
log.error("could not resolve "+lgeAddress,e);
lastFault = e;
break use_cache;
}
return call.call(lge);
} catch (Exception fault) {
cache.clear(key);
fault = plugin.convert(fault,config());
if (isUnrecoverable(fault) || isAnchored()) // exit now
throw fault;
else
lastFault = fault; //move on to querying
}
List results = null;
try {
log.info("executing query for {} endpoints: {}", plugin.name(),query);
// execute query
results = query.fire();
// exclude cached endpoint (we do not use 'remove' in case list implementation does not support it)
results = filterResults(lgeAddress, results);
if (results.size() == 0)
throw new DiscoveryException("no endpoints found for "+query);
}
catch(DiscoveryException fault) {
if (lastFault == null)
throw fault;
else
throw lastFault;
}
//try with each endpoint in turn
for (A address : results)
try {
log.info("calling {} @ {}", plugin.name(), address);
S stub = null;
try {
stub=plugin.resolve(address,config());
}
catch(Exception e) {
log.error("could not resolve "+address,e);
lastFault = e;
}
V result = call.call(stub);
cache.put(key, address);
return result;
} catch (Exception fault) {
lastFault= plugin.convert(fault,config());
if (isUnrecoverable(lastFault) || isAnchored()) // exit now
break;
}
throw lastFault;
}
//helper
private