package xdi2.discovery;
import java.net.URI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xdi2.client.XDIClient;
import xdi2.client.constants.XDIClientConstants;
import xdi2.client.events.XDIDiscoverFromAuthorityEvent;
import xdi2.client.events.XDIDiscoverFromRegistryEvent;
import xdi2.client.exceptions.Xdi2ClientException;
import xdi2.client.exceptions.Xdi2DiscoveryException;
import xdi2.client.impl.http.XDIHttpClient;
import xdi2.client.util.URLURIUtil;
import xdi2.core.constants.XDIConstants;
import xdi2.core.constants.XDIDictionaryConstants;
import xdi2.core.constants.XDISecurityConstants;
import xdi2.core.features.linkcontracts.instance.PublicLinkContract;
import xdi2.core.features.nodetypes.XdiPeerRoot;
import xdi2.core.syntax.CloudNumber;
import xdi2.core.syntax.XDIAddress;
import xdi2.core.syntax.XDIStatement;
import xdi2.core.util.XDIAddressUtil;
import xdi2.discovery.cache.DiscoveryCacheKey;
import xdi2.discovery.cache.DiscoveryCacheProvider;
import xdi2.messaging.Message;
import xdi2.messaging.MessageEnvelope;
import xdi2.messaging.response.TransportMessagingResponse;
/**
* Given a Cloud Name or discovery key, useful information such a Cloud Number,
* public keys, or additional services, can be discovered.
*
* @author markus
*/
public class XDIDiscoveryClient {
private static Logger log = LoggerFactory.getLogger(XDIDiscoveryClient.class.getName());
public static final XDIHttpClient XDI2_DISCOVERY_XDI_CLIENT;
public static final XDIHttpClient NEUSTAR_PROD_DISCOVERY_XDI_CLIENT;
public static final XDIHttpClient NEUSTAR_OTE_DISCOVERY_XDI_CLIENT;
public static final XDIHttpClient NEUSTAR_STAGE_DISCOVERY_XDI_CLIENT;
public static final XDIHttpClient LEOLA_NYMBLE_DISCOVERY_XDI_CLIENT;
public static final XDIDiscoveryClient XDI2_DISCOVERY_CLIENT;
public static final XDIDiscoveryClient NEUSTAR_PROD_DISCOVERY_CLIENT;
public static final XDIDiscoveryClient NEUSTAR_OTE_DISCOVERY_CLIENT;
public static final XDIDiscoveryClient NEUSTAR_STAGE_DISCOVERY_CLIENT;
public static final XDIDiscoveryClient LEOLA_NYMBLE_DISCOVERY_CLIENT;
public static final XDIHttpClient DEFAULT_XDI_CLIENT;
public static final XDIDiscoveryClient DEFAULT_DISCOVERY_CLIENT;
public static final DiscoveryCacheProvider DEFAULT_DISCOVERY_CACHE_PROVIDER;
private XDIClient<? extends TransportMessagingResponse> registryXdiClient;
private DiscoveryCacheProvider discoveryCacheProvider;
static {
DEFAULT_DISCOVERY_CACHE_PROVIDER = DiscoveryCacheProvider.get();
XDI2_DISCOVERY_XDI_CLIENT = new XDIHttpClient(URLURIUtil.URI("https://registry.xdi2.org/"));
NEUSTAR_PROD_DISCOVERY_XDI_CLIENT = new XDIHttpClient(URLURIUtil.URI("https://xdidiscoveryservice.xdi.net/"));
NEUSTAR_OTE_DISCOVERY_XDI_CLIENT = new XDIHttpClient(URLURIUtil.URI("https://xdidiscoveryserviceote.xdi.net/"));
NEUSTAR_STAGE_DISCOVERY_XDI_CLIENT = new XDIHttpClient(URLURIUtil.URI("https://xdidiscovery-stg.cloudnames.biz/"));
LEOLA_NYMBLE_DISCOVERY_XDI_CLIENT = new XDIHttpClient(URLURIUtil.URI("http://xdi.nymble.me/"));
XDI2_DISCOVERY_CLIENT = new XDIDiscoveryClient(XDI2_DISCOVERY_XDI_CLIENT);
NEUSTAR_PROD_DISCOVERY_CLIENT = new XDIDiscoveryClient(NEUSTAR_PROD_DISCOVERY_XDI_CLIENT);
NEUSTAR_OTE_DISCOVERY_CLIENT = new XDIDiscoveryClient(NEUSTAR_OTE_DISCOVERY_XDI_CLIENT);
NEUSTAR_STAGE_DISCOVERY_CLIENT = new XDIDiscoveryClient(NEUSTAR_STAGE_DISCOVERY_XDI_CLIENT);
LEOLA_NYMBLE_DISCOVERY_CLIENT = new XDIDiscoveryClient(LEOLA_NYMBLE_DISCOVERY_XDI_CLIENT);
DEFAULT_XDI_CLIENT = XDI2_DISCOVERY_XDI_CLIENT;
DEFAULT_DISCOVERY_CLIENT = XDI2_DISCOVERY_CLIENT;
}
public XDIDiscoveryClient(XDIClient<TransportMessagingResponse> registryXdiClient, DiscoveryCacheProvider discoveryCacheProvider) {
this.registryXdiClient = registryXdiClient;
this.discoveryCacheProvider = discoveryCacheProvider;
if (log.isDebugEnabled()) log.debug("Initializing discovery with client " + (registryXdiClient == null ? null : registryXdiClient.getClass().getSimpleName()) + " and cache " + (discoveryCacheProvider == null ? null : discoveryCacheProvider.getClass().getSimpleName()));
}
public XDIDiscoveryClient(XDIClient<TransportMessagingResponse> registryXdiClient) {
this(registryXdiClient, DEFAULT_DISCOVERY_CACHE_PROVIDER);
}
public XDIDiscoveryClient(URI registryEndpointUri, DiscoveryCacheProvider discoveryCacheProvider) {
this(new XDIHttpClient(registryEndpointUri), discoveryCacheProvider);
}
public XDIDiscoveryClient(URI registryEndpointUri) {
this(new XDIHttpClient(registryEndpointUri));
}
public XDIDiscoveryClient(String registryEndpointUri) {
this(new XDIHttpClient(registryEndpointUri));
}
public XDIDiscoveryClient() {
this(DEFAULT_XDI_CLIENT);
}
public XDIDiscoveryResult discover(XDIAddress query, XDIAddress[] endpointUriTypes) throws Xdi2DiscoveryException, Xdi2ClientException {
// first discover from registry
XDIDiscoveryResult xdiDiscoveryResultRegistry = this.discoverFromRegistry(query, endpointUriTypes);
if (xdiDiscoveryResultRegistry == null) {
if (log.isDebugEnabled()) log.debug("No discovery result from registry for " + query);
return null;
}
if (xdiDiscoveryResultRegistry.getXdiEndpointUri() == null || xdiDiscoveryResultRegistry.getCloudNumber() == null) {
if (log.isDebugEnabled()) log.debug("No XDI endpoint URI or cloud number from registry for " + query);
return xdiDiscoveryResultRegistry;
}
// then discover from authority
XDIDiscoveryResult xdiDiscoveryResultAuthority = this.discoverFromAuthority(xdiDiscoveryResultRegistry.getXdiEndpointUri(), xdiDiscoveryResultRegistry.getCloudNumber(), endpointUriTypes);
if (xdiDiscoveryResultAuthority == null) {
if (log.isDebugEnabled()) log.debug("No discovery result from authority for " + query);
return xdiDiscoveryResultRegistry;
}
// return a single discovery result from the two individual ones
XDIDiscoveryResult xdiDiscoveryResult = new XDIDiscoveryResult();
xdiDiscoveryResult.initFromRegistryAndAuthorityDiscoveryResult(xdiDiscoveryResultRegistry, xdiDiscoveryResultAuthority, query, endpointUriTypes);
return xdiDiscoveryResult;
}
public XDIDiscoveryResult discover(XDIAddress query, XDIAddress endpointUriType) throws Xdi2DiscoveryException, Xdi2ClientException {
return this.discover(query, new XDIAddress[] { endpointUriType });
}
public XDIDiscoveryResult discover(XDIAddress query) throws Xdi2DiscoveryException, Xdi2ClientException {
return this.discover(query, (XDIAddress[]) null);
}
public XDIDiscoveryResult discoverFromRegistry(XDIAddress query, XDIAddress[] endpointUriTypes) throws Xdi2DiscoveryException, Xdi2ClientException {
XDIDiscoveryResult xdiDiscoveryResult = new XDIDiscoveryResult();
// check registry cache
TransportMessagingResponse registryMessagingResponse = null;
DiscoveryCacheKey registryDiscoveryCacheKey = DiscoveryCacheKey.build(query, this.getRegistryXdiClient(), null);
synchronized(this) {
if (this.getDiscoveryCacheProvider() != null) registryMessagingResponse = (TransportMessagingResponse) this.getDiscoveryCacheProvider().getRegistry(registryDiscoveryCacheKey);
}
MessageEnvelope registryMessageEnvelope = null;
if (registryMessagingResponse != null) {
if (log.isDebugEnabled()) log.debug("Registry cache HIT: " + registryDiscoveryCacheKey);
} else {
if (log.isDebugEnabled()) log.debug("Registry cache MISS: " + registryDiscoveryCacheKey);
// send the registry message
registryMessageEnvelope = new MessageEnvelope();
Message registryMessage = registryMessageEnvelope.createMessage(null);
registryMessage.createGetOperation(XDIAddress.fromComponent(XdiPeerRoot.createPeerRootXDIArc(query)));
try {
XDIClient<? extends TransportMessagingResponse> registryXdiClient = this.getRegistryXdiClient();
registryMessagingResponse = registryXdiClient.send(registryMessageEnvelope);
} catch (Xdi2ClientException ex) {
xdiDiscoveryResult.initFromException(ex);
throw ex;
} catch (Exception ex) {
throw new Xdi2DiscoveryException("Cannot send XDI message to XDI registry: " + ex.getMessage(), ex);
}
// save in cache
if (this.getDiscoveryCacheProvider() != null) {
if (log.isDebugEnabled()) log.debug("Registry cache PUT: " + registryDiscoveryCacheKey);
this.getDiscoveryCacheProvider().putRegistry(registryDiscoveryCacheKey, registryMessagingResponse);
}
// fire event
this.getRegistryXdiClient().fireDiscoverEvent(new XDIDiscoverFromRegistryEvent(this, registryMessageEnvelope, xdiDiscoveryResult, query));
}
// init the registry discovery result
xdiDiscoveryResult.initFromRegistryMessagingResponse(registryMessageEnvelope, registryMessagingResponse, query, endpointUriTypes);
// cloud number check
if (CloudNumber.isValid(query) && xdiDiscoveryResult.getCloudNumber() != null) {
if (! xdiDiscoveryResult.getCloudNumber().getXDIAddress().equals(query)) throw new Xdi2DiscoveryException("Queried cloud number " + query + " does not match discovered cloud number " + xdiDiscoveryResult.getCloudNumber().getXDIAddress());
}
// done
if (log.isDebugEnabled()) log.debug("Discovery result from registry: " + xdiDiscoveryResult);
return xdiDiscoveryResult;
}
public XDIDiscoveryResult discoverFromRegistry(XDIAddress query, XDIAddress endpointUriType) throws Xdi2DiscoveryException, Xdi2ClientException {
return this.discoverFromRegistry(query, new XDIAddress[] { endpointUriType });
}
public XDIDiscoveryResult discoverFromRegistry(XDIAddress query) throws Xdi2DiscoveryException, Xdi2ClientException {
return this.discoverFromRegistry(query, (XDIAddress[]) null);
}
public XDIDiscoveryResult discoverFromAuthority(URI xdiEndpointUri, CloudNumber cloudNumber, XDIAddress[] endpointUriTypes) throws Xdi2DiscoveryException, Xdi2ClientException {
XDIDiscoveryResult xdiDiscoveryResult = new XDIDiscoveryResult();
// check authority cache
TransportMessagingResponse authorityMessagingResponse = null;
DiscoveryCacheKey authorityDiscoveryCacheKey = DiscoveryCacheKey.build(cloudNumber, xdiEndpointUri, endpointUriTypes);
synchronized(this) {
if (this.getDiscoveryCacheProvider() != null) authorityMessagingResponse = (TransportMessagingResponse) this.getDiscoveryCacheProvider().getAuthority(authorityDiscoveryCacheKey);
}
MessageEnvelope authorityMessageEnvelope = null;
if (authorityMessagingResponse != null) {
if (log.isDebugEnabled()) log.debug("Authority cache HIT: " + authorityDiscoveryCacheKey);
} else {
if (log.isDebugEnabled()) log.debug("Authority cache MISS: " + authorityDiscoveryCacheKey);
// send the authority message
authorityMessageEnvelope = new MessageEnvelope();
Message authorityMessage = authorityMessageEnvelope.createMessage(null);
authorityMessage.setToPeerRootXDIArc(cloudNumber.getPeerRootXDIArc());
authorityMessage.setLinkContractClass(PublicLinkContract.class);
authorityMessage.createGetOperation(XDIStatement.fromRelationComponents(XDIConstants.XDI_ADD_ROOT, XDIDictionaryConstants.XDI_ADD_IS_REF, XDIConstants.XDI_ADD_COMMON_VARIABLE));
authorityMessage.createGetOperation(XDIStatement.fromRelationComponents(cloudNumber.getXDIAddress(), XDIDictionaryConstants.XDI_ADD_IS_REF, XDIConstants.XDI_ADD_COMMON_VARIABLE));
authorityMessage.createGetOperation(XDIAddressUtil.concatXDIAddresses(cloudNumber.getXDIAddress(), XDISecurityConstants.XDI_ADD_MSG_SIG_KEYPAIR_PUBLIC_KEY));
authorityMessage.createGetOperation(XDIAddressUtil.concatXDIAddresses(cloudNumber.getXDIAddress(), XDISecurityConstants.XDI_ADD_MSG_ENCRYPT_KEYPAIR_PUBLIC_KEY));
if (endpointUriTypes != null) {
for (XDIAddress endpointUriType : endpointUriTypes) {
authorityMessage.createGetOperation(XDIAddressUtil.concatXDIAddresses(XDIAddress.fromComponent(cloudNumber.getPeerRootXDIArc()), endpointUriType, XDIClientConstants.XDI_ADD_AS_URI));
}
}
try {
XDIHttpClient authorityXdiHttpClient = new XDIHttpClient(xdiEndpointUri);
authorityMessagingResponse = authorityXdiHttpClient.send(authorityMessageEnvelope);
} catch (Xdi2ClientException ex) {
xdiDiscoveryResult.initFromException(ex);
throw ex;
} catch (Exception ex) {
throw new Xdi2DiscoveryException("Cannot send XDI message to XDI authority: " + ex.getMessage(), ex);
}
// save in cache
if (this.getDiscoveryCacheProvider() != null) {
if (log.isDebugEnabled()) log.debug("Authority cache PUT: " + authorityDiscoveryCacheKey);
this.getDiscoveryCacheProvider().putAuthority(authorityDiscoveryCacheKey, authorityMessagingResponse);
}
// fire event
this.getRegistryXdiClient().fireDiscoverEvent(new XDIDiscoverFromAuthorityEvent(this, authorityMessageEnvelope, xdiDiscoveryResult, xdiEndpointUri));
}
// init the authority discovery result
xdiDiscoveryResult.initFromAuthorityMessagingResponse(authorityMessageEnvelope, authorityMessagingResponse, endpointUriTypes);
// cloud number check
if (! xdiDiscoveryResult.getCloudNumber().getXDIAddress().equals(cloudNumber.getXDIAddress())) throw new Xdi2DiscoveryException("Queried cloud number " + cloudNumber.getXDIAddress() + " does not match discovered cloud number " + xdiDiscoveryResult.getCloudNumber().getXDIAddress());
// done
if (log.isDebugEnabled()) log.debug("Discovery result from authority: " + xdiDiscoveryResult);
return xdiDiscoveryResult;
}
public XDIDiscoveryResult discoverFromAuthority(URI xdiEndpointUri, CloudNumber cloudNumber, XDIAddress endpointUriType) throws Xdi2DiscoveryException, Xdi2ClientException {
return this.discoverFromAuthority(xdiEndpointUri, cloudNumber, new XDIAddress[] { endpointUriType });
}
public XDIDiscoveryResult discoverFromAuthority(URI xdiEndpointUri, CloudNumber cloudNumber) throws Xdi2DiscoveryException, Xdi2ClientException {
return this.discoverFromAuthority(xdiEndpointUri, cloudNumber, (XDIAddress[]) null);
}
/*
* Object methods
*/
@Override
public String toString() {
return String.valueOf(this.getRegistryXdiClient());
}
/*
* Getters and setters
*/
public XDIClient<? extends TransportMessagingResponse> getRegistryXdiClient() {
return this.registryXdiClient;
}
public void setRegistryXdiClient(XDIClient<? extends TransportMessagingResponse> registryXdiClient) {
this.registryXdiClient = registryXdiClient;
}
public DiscoveryCacheProvider getDiscoveryCacheProvider() {
return this.discoveryCacheProvider;
}
public void setDiscoveryCacheProvider(DiscoveryCacheProvider discoveryCacheProvider) {
this.discoveryCacheProvider = discoveryCacheProvider;
}
}