package xdi2.messaging.container.interceptor.impl;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xdi2.core.ContextNode;
import xdi2.core.Graph;
import xdi2.core.constants.XDIConstants;
import xdi2.core.constants.XDIDictionaryConstants;
import xdi2.core.constants.XDILinkContractConstants;
import xdi2.core.constants.XDITimestampsConstants;
import xdi2.core.features.equivalence.Equivalence;
import xdi2.core.features.linkcontracts.instance.ConnectLinkContract;
import xdi2.core.features.linkcontracts.instance.PublicLinkContract;
import xdi2.core.features.linkcontracts.instance.RootLinkContract;
import xdi2.core.features.linkcontracts.instance.SendLinkContract;
import xdi2.core.features.nodetypes.XdiCommonRoot;
import xdi2.core.features.nodetypes.XdiPeerRoot;
import xdi2.core.features.nodetypes.XdiPeerRoot.MappingContextNodePeerRootIterator;
import xdi2.core.features.policy.PolicyAnd;
import xdi2.core.features.policy.PolicyOr;
import xdi2.core.features.policy.PolicyRoot;
import xdi2.core.features.policy.PolicyUtil;
import xdi2.core.features.timestamps.Timestamps;
import xdi2.core.impl.memory.MemoryGraphFactory;
import xdi2.core.syntax.XDIAddress;
import xdi2.core.syntax.XDIArc;
import xdi2.core.syntax.XDIStatement;
import xdi2.core.util.CopyUtil;
import xdi2.core.util.CopyUtil.CompoundCopyStrategy;
import xdi2.core.util.CopyUtil.CopyStrategy;
import xdi2.core.util.CopyUtil.ReplaceRegexLiteralStringCopyStrategy;
import xdi2.core.util.CopyUtil.ReplaceXDIAddressCopyStrategy;
import xdi2.core.util.XDIAddressUtil;
import xdi2.core.util.iterators.IteratorArrayMaker;
import xdi2.messaging.MessageEnvelope;
import xdi2.messaging.container.MessagingContainer;
import xdi2.messaging.container.Prototype;
import xdi2.messaging.container.exceptions.Xdi2MessagingException;
import xdi2.messaging.container.execution.ExecutionContext;
import xdi2.messaging.container.execution.ExecutionResult;
import xdi2.messaging.container.impl.graph.GraphMessagingContainer;
import xdi2.messaging.container.interceptor.impl.linkcontract.LinkContractInterceptor;
/**
* This interceptor can initialize an empty XDI graph with basic bootstrapping data,
* such as the owner XDI address of the graph, and initial link contracts.
*
* @author markus
*/
public class BootstrapInterceptor extends AbstractInterceptor<MessagingContainer> implements Prototype<BootstrapInterceptor> {
private static Logger log = LoggerFactory.getLogger(BootstrapInterceptor.class.getName());
public final static int INIT_PRIORITY = 150;
public final static int SHUTDOWN_PRIORITY = 150;
public final static XDIArc XDI_ARC_SELF = XDIArc.create("{$self}");
private XDIAddress bootstrapOwner;
private XDIAddress[] bootstrapOwnerSynonyms;
private boolean bootstrapRootLinkContract;
private boolean bootstrapPublicLinkContract;
private boolean bootstrapConnectLinkContract;
private boolean bootstrapSendLinkContract;
private boolean bootstrapTimestamp;
private Graph bootstrapGraph;
private MessageEnvelope bootstrapMessageEnvelope;
public BootstrapInterceptor() {
super(INIT_PRIORITY, SHUTDOWN_PRIORITY);
this.bootstrapOwner = null;
this.bootstrapOwnerSynonyms = null;
this.bootstrapRootLinkContract = false;
this.bootstrapPublicLinkContract = false;
this.bootstrapConnectLinkContract = false;
this.bootstrapSendLinkContract = false;
this.bootstrapTimestamp = false;
this.bootstrapGraph = null;
this.bootstrapMessageEnvelope = null;
}
/*
* Prototype
*/
@Override
public BootstrapInterceptor instanceFor(PrototypingContext prototypingContext) {
// create new interceptor
BootstrapInterceptor interceptor = new BootstrapInterceptor();
// set the owner, root link contract, public link contract, connect link contract, send link contract
interceptor.setBootstrapOwner(prototypingContext.getOwnerXDIAddress());
interceptor.setBootstrapRootLinkContract(this.getBootstrapRootLinkContract());
interceptor.setBootstrapPublicLinkContract(this.getBootstrapPublicLinkContract());
interceptor.setBootstrapConnectLinkContract(this.getBootstrapConnectLinkContract());
interceptor.setBootstrapSendLinkContract(this.getBootstrapSendLinkContract());
// set the owner synonyms
XDIAddress[] bootstrapOwnerSynonyms = null;
if (prototypingContext.getOwnerXdiPeerRoot() != null) {
Iterator<ContextNode> ownerSynonymPeerRootContextNodes = Equivalence.getIncomingReferenceContextNodes(prototypingContext.getOwnerXdiPeerRoot().getContextNode());
XdiPeerRoot[] ownerSynonymPeerRoots = (new IteratorArrayMaker<XdiPeerRoot> (new MappingContextNodePeerRootIterator(ownerSynonymPeerRootContextNodes))).array(XdiPeerRoot.class);
bootstrapOwnerSynonyms = new XDIAddress[ownerSynonymPeerRoots.length];
for (int i=0; i<bootstrapOwnerSynonyms.length; i++) bootstrapOwnerSynonyms[i] = ownerSynonymPeerRoots[i].getXDIAddressOfPeerRoot();
}
interceptor.setBootstrapOwnerSynonyms(bootstrapOwnerSynonyms);
// set bootstrap statements and operations
interceptor.setBootstrapGraph(this.getBootstrapGraph());
interceptor.setBootstrapMessageEnvelope(this.getBootstrapMessageEnvelope());
// done
return interceptor;
}
/*
* Init and shutdown
*/
@Override
public void init(MessagingContainer messagingContainer) throws Exception {
super.init(messagingContainer);
if (! (messagingContainer instanceof GraphMessagingContainer)) return;
GraphMessagingContainer graphMessagingContainer = (GraphMessagingContainer) messagingContainer;
Graph graph = graphMessagingContainer.getGraph();
if (log.isDebugEnabled()) log.debug("bootstrapOwner=" + this.getBootstrapOwner() + ", bootstrapOwnerSynonyms=" + (this.getBootstrapOwnerSynonyms() == null ? null : Arrays.asList(this.getBootstrapOwnerSynonyms())) + ", bootstrapLinkContract=" + this.getBootstrapRootLinkContract() + ", bootstrapPublicLinkContract=" + this.getBootstrapPublicLinkContract() + ", bootstrapConnectLinkContract=" + this.getBootstrapConnectLinkContract() + ", bootstrapSendLinkContract=" + this.getBootstrapSendLinkContract() + ", bootstrapGraph=" + (this.getBootstrapGraph() != null) + ", bootstrapMessageEnvelope=" + (this.getBootstrapMessageEnvelope() != null));
// check if the owner statement exists
if (XdiCommonRoot.findCommonRoot(graph).getSelfPeerRoot() != null) return;
// create bootstrap owner
ContextNode bootstrapOwnerContextNode = null;
ContextNode bootstrapOwnerSelfPeerRootContextNode = null;
if (this.getBootstrapOwner() != null) {
if (log.isDebugEnabled()) log.debug("Creating bootstrap owner: " + this.getBootstrapOwner());
bootstrapOwnerContextNode = graph.setDeepContextNode(this.getBootstrapOwner());
bootstrapOwnerSelfPeerRootContextNode = XdiCommonRoot.findCommonRoot(graph).setSelfPeerRoot(this.getBootstrapOwner()).getContextNode();
// create bootstrap owner synonyms
if (this.getBootstrapOwnerSynonyms() != null) {
if (log.isDebugEnabled()) log.debug("Creating bootstrap owner synonyms: " + Arrays.asList(this.getBootstrapOwnerSynonyms()));
for (XDIAddress bootstrapOwnerSynonym : this.getBootstrapOwnerSynonyms()) {
ContextNode bootstrapOwnerSynonymContextNode = graph.setDeepContextNode(bootstrapOwnerSynonym);
Equivalence.setReferenceContextNode(bootstrapOwnerSynonymContextNode, bootstrapOwnerContextNode, true);
ContextNode bootstrapOwnerSynonymPeerRootContextNode = XdiCommonRoot.findCommonRoot(graph).getPeerRoot(bootstrapOwnerSynonym, true).getContextNode();
Equivalence.setReferenceContextNode(bootstrapOwnerSynonymPeerRootContextNode, bootstrapOwnerSelfPeerRootContextNode, false);
}
}
}
// create bootstrap root link contract
if (this.getBootstrapRootLinkContract()) {
if (this.getBootstrapOwner() == null) {
throw new Xdi2MessagingException("Can only create the bootstrap root link contract if a bootstrap owner is given.", null, null);
}
if (log.isDebugEnabled()) log.debug("Creating bootstrap root link contract.");
RootLinkContract bootstrapRootLinkContract = RootLinkContract.findRootLinkContract(graph, true);
bootstrapRootLinkContract.setPermissionTargetXDIAddress(XDILinkContractConstants.XDI_ADD_ALL, XDIConstants.XDI_ADD_ROOT);
PolicyRoot policyRoot = bootstrapRootLinkContract.getPolicyRoot(true);
PolicyAnd policyAnd = policyRoot.createAndPolicy(true);
PolicyUtil.createSenderIsOperator(policyAnd, this.getBootstrapOwner());
PolicyOr policyOr = policyAnd.createOrPolicy(true);
PolicyUtil.createSecretTokenValidOperator(policyOr);
PolicyUtil.createSignatureValidOperator(policyOr);
}
// create bootstrap public link contract
if (this.getBootstrapPublicLinkContract()) {
if (this.getBootstrapOwner() == null) {
throw new Xdi2MessagingException("Can only create the bootstrap public link contract if a bootstrap owner is given.", null, null);
}
if (log.isDebugEnabled()) log.debug("Creating bootstrap public link contract.");
PublicLinkContract bootstrapPublicLinkContract = PublicLinkContract.findPublicLinkContract(graph, true);
XDIAddress publicAddress = XDIAddressUtil.concatXDIAddresses(this.getBootstrapOwner(), XDILinkContractConstants.XDI_ADD_PUBLIC);
bootstrapPublicLinkContract.setPermissionTargetXDIAddress(XDILinkContractConstants.XDI_ADD_GET, publicAddress);
XDIStatement selfPeerRootRefStatement = XDIStatement.fromRelationComponents(XDIConstants.XDI_ADD_ROOT, XDIDictionaryConstants.XDI_ADD_IS_REF, XDIConstants.XDI_ADD_COMMON_VARIABLE);
bootstrapPublicLinkContract.setPermissionTargetXDIStatement(XDILinkContractConstants.XDI_ADD_GET, selfPeerRootRefStatement);
XDIStatement bootstrapOwnerSynonymsIsRefStatement = XDIStatement.fromRelationComponents(this.getBootstrapOwner(), XDIDictionaryConstants.XDI_ADD_IS_REF, XDIConstants.XDI_ADD_COMMON_VARIABLE);
bootstrapPublicLinkContract.setPermissionTargetXDIStatement(XDILinkContractConstants.XDI_ADD_GET, bootstrapOwnerSynonymsIsRefStatement);
if (this.getBootstrapOwnerSynonyms() != null) {
for (XDIAddress bootstrapOwnerSynonym : this.getBootstrapOwnerSynonyms()) {
XDIStatement bootstrapOwnerSynonymRefStatement = XDIStatement.fromRelationComponents(bootstrapOwnerSynonym, XDIDictionaryConstants.XDI_ADD_REF, this.getBootstrapOwner());
bootstrapPublicLinkContract.setPermissionTargetXDIStatement(XDILinkContractConstants.XDI_ADD_GET, bootstrapOwnerSynonymRefStatement);
}
}
}
// create bootstrap connect link contract
if (this.getBootstrapConnectLinkContract()) {
if (this.getBootstrapOwner() == null) {
throw new Xdi2MessagingException("Can only create the bootstrap connect link contract if a bootstrap owner is given.", null, null);
}
if (log.isDebugEnabled()) log.debug("Creating bootstrap connect link contract.");
ConnectLinkContract bootstrapConnectLinkContract = ConnectLinkContract.findConnectLinkContract(graph, true);
bootstrapConnectLinkContract.setPermissionTargetXDIAddress(XDILinkContractConstants.XDI_ADD_CONNECT, XDIConstants.XDI_ADD_ROOT);
PolicyRoot policyRoot = bootstrapConnectLinkContract.getPolicyRoot(true);
policyRoot.createNotPolicy(true);
PolicyRoot deferPushPolicyRoot = bootstrapConnectLinkContract.getDeferPushPolicyRoot(true);
PolicyAnd deferPushPolicyAnd = deferPushPolicyRoot.createAndPolicy(true);
PolicyUtil.createSignatureValidOperator(deferPushPolicyAnd);
}
// create bootstrap send link contract
if (this.getBootstrapSendLinkContract()) {
if (this.getBootstrapOwner() == null) {
throw new Xdi2MessagingException("Can only create the bootstrap send link contract if a bootstrap owner is given.", null, null);
}
if (log.isDebugEnabled()) log.debug("Creating bootstrap send link contract.");
SendLinkContract bootstrapSendLinkContract = SendLinkContract.findSendLinkContract(graph, true);
bootstrapSendLinkContract.setPermissionTargetXDIAddress(XDILinkContractConstants.XDI_ADD_SEND, XDIConstants.XDI_ADD_ROOT);
PolicyRoot policyRoot = bootstrapSendLinkContract.getPolicyRoot(true);
policyRoot.createNotPolicy(true);
PolicyRoot deferPolicyRoot = bootstrapSendLinkContract.getDeferPolicyRoot(true);
PolicyAnd deferPolicyAnd = deferPolicyRoot.createAndPolicy(true);
PolicyUtil.createSignatureValidOperator(deferPolicyAnd);
}
// create bootstrap timestamp
if (this.getBootstrapTimestamp()) {
Timestamps.setTimestamp(XdiCommonRoot.findCommonRoot(graph), XDITimestampsConstants.XDI_ADD_AS_CREATION, new Date());
}
// create bootstrap graph
if (this.getBootstrapGraph() != null) {
CopyStrategy copyStrategy = new CompoundCopyStrategy(
new ReplaceXDIAddressCopyStrategy(XDI_ARC_SELF, this.getBootstrapOwner()),
new ReplaceRegexLiteralStringCopyStrategy(Pattern.quote(XDI_ARC_SELF.toString()), this.getBootstrapOwner().toString()));
Graph bootstrapGraph = MemoryGraphFactory.getInstance().openGraph();
CopyUtil.copyGraph(this.getBootstrapGraph(), bootstrapGraph, copyStrategy);
if (log.isDebugEnabled()) log.debug("Creating bootstrap graph: " + bootstrapGraph.toString());
CopyUtil.copyGraph(bootstrapGraph, graph, null);
bootstrapGraph.close();
}
// execute bootstrap message envelope
if (this.getBootstrapMessageEnvelope() != null) {
CopyStrategy copyStrategy = new ReplaceXDIAddressCopyStrategy(XDI_ARC_SELF, BootstrapInterceptor.this.getBootstrapOwner());
MessageEnvelope bootstrapMessageEnvelope = new MessageEnvelope();
CopyUtil.copyGraph(this.getBootstrapMessageEnvelope().getGraph(), bootstrapMessageEnvelope.getGraph(), copyStrategy);
if (log.isDebugEnabled()) log.debug("Executing bootstrap message envelope: " + bootstrapMessageEnvelope.getGraph().toString());
ToInterceptor toInterceptor = graphMessagingContainer.getInterceptors().getInterceptor(ToInterceptor.class);
if (toInterceptor != null) toInterceptor.setDisabledForMessageEnvelope(bootstrapMessageEnvelope);
RefInterceptor refInterceptor = graphMessagingContainer.getInterceptors().getInterceptor(RefInterceptor.class);
if (refInterceptor != null) refInterceptor.setDisabledForMessageEnvelope(bootstrapMessageEnvelope);
LinkContractInterceptor linkContractInterceptor = graphMessagingContainer.getInterceptors().getInterceptor(LinkContractInterceptor.class);
if (linkContractInterceptor != null) linkContractInterceptor.setDisabledForMessageEnvelope(bootstrapMessageEnvelope);
ExecutionContext executionContext = ExecutionContext.createExecutionContext();
ExecutionResult executionResult = ExecutionResult.createExecutionResult(bootstrapMessageEnvelope);
graphMessagingContainer.execute(bootstrapMessageEnvelope, executionContext, executionResult);
}
}
@Override
public void shutdown(MessagingContainer messagingContainer) throws Exception {
super.shutdown(messagingContainer);
}
/*
* Getters and setters
*/
public XDIAddress getBootstrapOwner() {
return this.bootstrapOwner;
}
public void setBootstrapOwner(XDIAddress bootstrapOwner) {
this.bootstrapOwner = bootstrapOwner;
}
public XDIAddress[] getBootstrapOwnerSynonyms() {
return this.bootstrapOwnerSynonyms;
}
public void setBootstrapOwnerSynonyms(XDIAddress[] bootstrapOwnerSynonyms) {
this.bootstrapOwnerSynonyms = bootstrapOwnerSynonyms;
}
public void setBootstrapOwnerSynonyms(String[] bootstrapOwnerSynonyms) {
this.bootstrapOwnerSynonyms = new XDIAddress[bootstrapOwnerSynonyms.length];
for (int i=0; i<this.bootstrapOwnerSynonyms.length; i++) this.bootstrapOwnerSynonyms[i] = XDIAddress.create(bootstrapOwnerSynonyms[i]);
}
public boolean getBootstrapRootLinkContract() {
return this.bootstrapRootLinkContract;
}
public void setBootstrapRootLinkContract(boolean bootstrapLinkContract) {
this.bootstrapRootLinkContract = bootstrapLinkContract;
}
public boolean getBootstrapPublicLinkContract() {
return this.bootstrapPublicLinkContract;
}
public void setBootstrapPublicLinkContract(boolean bootstrapPublicLinkContract) {
this.bootstrapPublicLinkContract = bootstrapPublicLinkContract;
}
public boolean getBootstrapConnectLinkContract() {
return this.bootstrapConnectLinkContract;
}
public void setBootstrapConnectLinkContract(boolean bootstrapConnectLinkContract) {
this.bootstrapConnectLinkContract = bootstrapConnectLinkContract;
}
public boolean getBootstrapSendLinkContract() {
return this.bootstrapSendLinkContract;
}
public void setBootstrapSendLinkContract(boolean bootstrapSendLinkContract) {
this.bootstrapSendLinkContract = bootstrapSendLinkContract;
}
public boolean getBootstrapTimestamp() {
return this.bootstrapTimestamp;
}
public void setBootstrapTimestamp(boolean bootstrapTimestamp) {
this.bootstrapTimestamp = bootstrapTimestamp;
}
public Graph getBootstrapGraph() {
return this.bootstrapGraph;
}
public void setBootstrapGraph(Graph bootstrapGraph) {
this.bootstrapGraph = bootstrapGraph;
}
public MessageEnvelope getBootstrapMessageEnvelope() {
return this.bootstrapMessageEnvelope;
}
public void setBootstrapMessageEnvelope(MessageEnvelope bootstrapMessageEnvelope) {
this.bootstrapMessageEnvelope = bootstrapMessageEnvelope;
}
}