package xdi2.discovery;
import java.io.Serializable;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import xdi2.client.constants.XDIClientConstants;
import xdi2.client.exceptions.Xdi2ClientException;
import xdi2.client.exceptions.Xdi2DiscoveryException;
import xdi2.core.Graph;
import xdi2.core.LiteralNode;
import xdi2.core.constants.XDIDictionaryConstants;
import xdi2.core.features.keys.Keys;
import xdi2.core.features.nodetypes.XdiAttribute;
import xdi2.core.features.nodetypes.XdiCommonRoot;
import xdi2.core.features.nodetypes.XdiContext;
import xdi2.core.features.nodetypes.XdiEntity;
import xdi2.core.features.nodetypes.XdiPeerRoot;
import xdi2.core.features.nodetypes.XdiRoot;
import xdi2.core.syntax.CloudName;
import xdi2.core.syntax.CloudNumber;
import xdi2.core.syntax.XDIAddress;
import xdi2.core.util.XDIAddressUtil;
import xdi2.core.util.iterators.IteratorArrayMaker;
import xdi2.core.util.iterators.MappingCloudNameIterator;
import xdi2.core.util.iterators.MappingRelationTargetXDIAddressIterator;
import xdi2.core.util.iterators.NotNullIterator;
import xdi2.core.util.iterators.ReadOnlyIterator;
import xdi2.messaging.MessageEnvelope;
import xdi2.messaging.response.TransportMessagingResponse;
public class XDIDiscoveryResult implements Serializable {
private static final long serialVersionUID = -1141807747864855392L;
private CloudNumber cloudNumber;
private CloudName[] cloudNames;
private PublicKey signaturePublicKey;
private PublicKey encryptionPublicKey;
private Map<XDIAddress, URI> endpointUris;
private MessageEnvelope messageEnvelope;
private TransportMessagingResponse messagingResponse;
public XDIDiscoveryResult() {
this.cloudNumber = null;
this.cloudNames = null;
this.signaturePublicKey = null;
this.encryptionPublicKey = null;
this.endpointUris = new HashMap<XDIAddress, URI> ();
this.messageEnvelope = null;
this.messagingResponse = null;
}
void initFromRegistryAndAuthorityDiscoveryResult(XDIDiscoveryResult xdiDiscoveryResultRegistry, XDIDiscoveryResult xdiDiscoveryResultAuthority, XDIAddress query, XDIAddress[] endpointUriTypes) throws Xdi2ClientException {
this.initFromRegistryMessagingResponse(xdiDiscoveryResultRegistry.getMessageEnvelope(), xdiDiscoveryResultRegistry.getMessagingResponse(), query, endpointUriTypes);
this.initFromAuthorityMessagingResponse(xdiDiscoveryResultAuthority.getMessageEnvelope(), xdiDiscoveryResultAuthority.getMessagingResponse(), endpointUriTypes);
}
void initFromRegistryMessagingResponse(MessageEnvelope registryMessageEnvelope, TransportMessagingResponse registryMessagingResponse, XDIAddress query, XDIAddress[] endpointUriTypes) throws Xdi2ClientException {
this.messageEnvelope = registryMessageEnvelope;
this.messagingResponse = registryMessagingResponse;
Graph registryResultGraph = registryMessagingResponse.getResultGraph();
// find cloud number
XdiRoot xdiRoot = XdiCommonRoot.findCommonRoot(registryResultGraph).getPeerRoot(query, false);
if (xdiRoot == null) return;
if (xdiRoot instanceof XdiPeerRoot && CloudNumber.isValid(((XdiPeerRoot) xdiRoot).getXDIAddressOfPeerRoot())) {
this.cloudNumber = CloudNumber.fromPeerRootXDIArc(((XdiPeerRoot) xdiRoot).getXDIArc());
}
xdiRoot = xdiRoot == null ? null : xdiRoot.dereference();
if (xdiRoot instanceof XdiPeerRoot && CloudNumber.isValid(((XdiPeerRoot) xdiRoot).getXDIAddressOfPeerRoot())) {
this.cloudNumber = CloudNumber.fromPeerRootXDIArc(((XdiPeerRoot) xdiRoot).getXDIArc());
}
// make sure we look for the XDI endpoint URI
if (endpointUriTypes == null) {
endpointUriTypes = new XDIAddress[] { XDIClientConstants.XDI_ADD_AS_XDI };
} else {
Set<XDIAddress> endpointUriTypesSet = new HashSet<XDIAddress> (Arrays.asList(endpointUriTypes));
endpointUriTypesSet.add(XDIClientConstants.XDI_ADD_AS_XDI);
endpointUriTypes = endpointUriTypesSet.toArray(new XDIAddress[endpointUriTypesSet.size()]);
}
// init endpoint URIs
initEndpointUris(xdiRoot, endpointUriTypes);
}
void initFromAuthorityMessagingResponse(MessageEnvelope authorityMessageEnvelope, TransportMessagingResponse authorityMessagingResponse, XDIAddress[] endpointUriTypes) throws Xdi2ClientException {
this.messageEnvelope = authorityMessageEnvelope;
this.messagingResponse = authorityMessagingResponse;
Graph authorityResultGraph = authorityMessagingResponse.getResultGraph();
// find cloud number
XdiRoot xdiRoot = XdiCommonRoot.findCommonRoot(authorityResultGraph).getSelfPeerRoot();
if (xdiRoot == null) xdiRoot = XdiCommonRoot.findCommonRoot(authorityResultGraph);
if (xdiRoot == null) return;
if (xdiRoot instanceof XdiPeerRoot && CloudNumber.isValid(((XdiPeerRoot) xdiRoot).getXDIAddressOfPeerRoot())) {
this.cloudNumber = CloudNumber.fromPeerRootXDIArc(((XdiPeerRoot) xdiRoot).getXDIArc());
}
xdiRoot = xdiRoot == null ? null : xdiRoot.dereference();
if (xdiRoot instanceof XdiPeerRoot && CloudNumber.isValid(((XdiPeerRoot) xdiRoot).getXDIAddressOfPeerRoot())) {
this.cloudNumber = CloudNumber.fromPeerRootXDIArc(((XdiPeerRoot) xdiRoot).getXDIArc());
}
// find authority
XdiEntity authorityXdiEntity = XdiCommonRoot.findCommonRoot(authorityResultGraph).getXdiEntity(this.cloudNumber.getXDIAddress(), false);
if (authorityXdiEntity == null) return;
// find cloud names
ReadOnlyIterator<CloudName> cloudNameIterator =
new NotNullIterator<CloudName> (
new MappingCloudNameIterator(
new MappingRelationTargetXDIAddressIterator(
authorityXdiEntity.getContextNode().getRelations(XDIDictionaryConstants.XDI_ADD_IS_REF))));
this.cloudNames = new IteratorArrayMaker<CloudName> (cloudNameIterator).array(CloudName.class);
// find signature public key
try {
this.signaturePublicKey = Keys.getSignaturePublicKey(authorityXdiEntity);
} catch (GeneralSecurityException ex) {
throw new Xdi2ClientException("Invalid signature public key: " + ex.getMessage(), ex, null);
}
// find encryption public key
try {
this.encryptionPublicKey = Keys.getEncryptionPublicKey(authorityXdiEntity);
} catch (GeneralSecurityException ex) {
throw new Xdi2ClientException("Invalid encryption public key: " + ex.getMessage(), ex, null);
}
// init endpoint uris
this.initEndpointUris(xdiRoot, endpointUriTypes);
}
void initFromException(Xdi2ClientException ex) {
// done
this.messagingResponse = ex.getMessagingResponse() instanceof TransportMessagingResponse ? (TransportMessagingResponse) ex.getMessagingResponse() : null;
}
/*
* Getters
*/
public CloudNumber getCloudNumber() {
return this.cloudNumber;
}
public CloudName[] getCloudNames() {
return this.cloudNames;
}
public PublicKey getSignaturePublicKey() {
return this.signaturePublicKey;
}
public PublicKey getEncryptionPublicKey() {
return this.encryptionPublicKey;
}
public Map<XDIAddress, URI> getEndpointUris() {
return this.endpointUris;
}
public URI getXdiEndpointUri() {
return this.getEndpointUris().get(XDIClientConstants.XDI_ENDPOINT_URI_TYPE);
}
public URI getXdiWebSocketEndpointUri() {
return this.getEndpointUris().get(XDIClientConstants.WEBSOCKET_ENDPOINT_URI_TYPE);
}
public URI getXdiConnectEndpointUri() {
return this.getEndpointUris().get(XDIClientConstants.CONNECT_ENDPOINT_URI_TYPE);
}
public URI getDefaultEndpointUri() {
return this.getEndpointUris().get(null);
}
public MessageEnvelope getMessageEnvelope() {
return this.messageEnvelope;
}
public TransportMessagingResponse getMessagingResponse() {
return this.messagingResponse;
}
/*
* Helper methods
*/
private void initEndpointUris(XdiContext<?> xdiContext, XDIAddress[] endpointUriTypes) throws Xdi2DiscoveryException {
if (endpointUriTypes == null) return;
for (XDIAddress endpointUriType : endpointUriTypes) {
XDIAddress endpointUriXdiAttributeAddress = XDIAddressUtil.concatXDIAddresses(endpointUriType, XDIClientConstants.XDI_ARC_AS_URI);
XdiAttribute endpointUriXdiAttribute = xdiContext.getXdiAttributeSingleton(endpointUriXdiAttributeAddress, false);
if (endpointUriXdiAttribute == null) continue;
endpointUriXdiAttribute = endpointUriXdiAttribute.dereference();
LiteralNode endpointUriLiteral = endpointUriXdiAttribute.getLiteralNode();
if (endpointUriLiteral == null) continue;
String endpointUri = endpointUriLiteral.getLiteralDataString();
if (endpointUri == null) continue;
this.endpointUris.put(endpointUriType, URI.create(endpointUri));
}
}
/*
* Object methods
*/
@Override
public String toString() {
return this.getCloudNumber() + " (" + this.getXdiEndpointUri() + ")";
}
}