/*
* Part of the CCNx Java Library.
*
* Copyright (C) 2009-2012 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received
* a copy of the GNU Lesser General Public License along with this library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.ccnx.ccn.profiles.ccnd;
import static org.ccnx.ccn.profiles.ccnd.FaceManager.CCNX;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.ccnx.ccn.CCNHandle;
import org.ccnx.ccn.impl.CCNNetworkManager;
import org.ccnx.ccn.impl.CCNNetworkManager.RegisteredPrefix;
import org.ccnx.ccn.impl.encoding.BinaryXMLCodec;
import org.ccnx.ccn.impl.encoding.CCNProtocolDTags;
import org.ccnx.ccn.impl.encoding.GenericXMLEncodable;
import org.ccnx.ccn.impl.encoding.XMLCodecFactory;
import org.ccnx.ccn.impl.encoding.XMLDecoder;
import org.ccnx.ccn.impl.encoding.XMLEncodable;
import org.ccnx.ccn.impl.encoding.XMLEncoder;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.io.content.ContentDecodingException;
import org.ccnx.ccn.io.content.ContentEncodingException;
import org.ccnx.ccn.protocol.ContentName;
import org.ccnx.ccn.protocol.MalformedContentNameStringException;
import org.ccnx.ccn.protocol.PublisherPublicKeyDigest;
public class PrefixRegistrationManager extends CCNDaemonHandle {
public enum ActionType {
Register ("prefixreg"), SelfRegister("selfreg"), UnRegister("unreg");
ActionType(String st) { this.st = st; }
private final String st;
public String value() { return st; }
}
// Forwarding flags - refer to doc/technical/Registration.txt for the meaning of these
public static final int CCN_FORW_ACTIVE = 1;
public static final int CCN_FORW_CHILD_INHERIT = 2; // This entry may be used even if there is a longer
// match available
public static final int CCN_FORW_ADVERTISE = 4; // Prefix may be advertised to other nodes
public static final int CCN_FORW_LAST = 8; // Entry should be used last if nothing else worked
public static final int CCN_FORW_CAPTURE = 16; // No shorter prefix may be used, overriding
// child-inherit bits that would otherwise make the
// shorter entries usable.
public static final int CCN_FORW_LOCAL = 32; // Restricts namespace to use by applications on the
// local machine
public static final int CCN_FORW_TAP = 64; // Causes the entry to be used right away - intended
// for debugging and monitoring purposes.
public static final int CCN_FORW_CAPTURE_OK = 128; // Use this with CCN_FORW_CHILD_INHERIT to make it eligible for capture.
public static final int CCN_FORW_PUBMASK = CCN_FORW_ACTIVE |
CCN_FORW_CHILD_INHERIT |
CCN_FORW_ADVERTISE |
CCN_FORW_LAST |
CCN_FORW_CAPTURE |
CCN_FORW_LOCAL |
CCN_FORW_TAP |
CCN_FORW_CAPTURE_OK;
public static final Integer DEFAULT_SELF_REG_FLAGS = Integer.valueOf(CCN_FORW_ACTIVE + CCN_FORW_CHILD_INHERIT);
/*
* #define CCN_FORW_ACTIVE 1
* #define CCN_FORW_CHILD_INHERIT 2
* #define CCN_FORW_ADVERTISE 4
* #define CCN_FORW_LAST 8
*/
public static class ForwardingEntry extends GenericXMLEncodable implements XMLEncodable {
/* extends CCNEncodableObject<PolicyXML> */
/**
* From the XML definitions:
* <xs:element name="ForwardingEntry" type="ForwardingEntryType"/>
* <xs:complexType name="ForwardingEntryType">
* <xs:sequence>
* <xs:element name="Action" type="xs:string" minOccurs="0" maxOccurs="1"/>
* <xs:element name="Name" type="NameType" minOccurs="0" maxOccurs="1"/>
* <xs:element name="PublisherPublicKeyDigest" type="DigestType" minOccurs="0" maxOccurs="1"/>
* <xs:element name="FaceID" type="xs:nonNegativeInteger" minOccurs="0" maxOccurs="1"/>
* <xs:element name="ForwardingFlags" type="xs:nonNegativeInteger" minOccurs="0" maxOccurs="1"/>
* <xs:element name="FreshnessSeconds" type="xs:nonNegativeInteger" minOccurs="0" maxOccurs="1"/>
* </xs:sequence>
* </xs:complexType>
*/
protected String _action;
protected ContentName _prefixName;
protected PublisherPublicKeyDigest _ccndId;
protected Integer _faceID;
protected Integer _flags;
protected Integer _lifetime = Integer.MAX_VALUE; // in seconds
public ForwardingEntry(ContentName prefixName, Integer faceID, Integer flags) {
_action = ActionType.Register.value();
_prefixName = new ContentName(prefixName); // in case ContentName gets subclassed
_faceID = faceID;
_flags = flags;
}
public ForwardingEntry(ActionType action, ContentName prefixName, PublisherPublicKeyDigest ccndId,
Integer faceID, Integer flags, Integer lifetime) {
_action = action.value();
_ccndId = ccndId;
_prefixName = new ContentName(prefixName); // in case ContentName gets subclassed
_faceID = faceID;
_flags = flags;
_lifetime = lifetime;
}
public ForwardingEntry(byte[] raw) {
ByteArrayInputStream bais = new ByteArrayInputStream(raw);
XMLDecoder decoder = XMLCodecFactory.getDecoder(BinaryXMLCodec.CODEC_NAME);
try {
decoder.beginDecoding(bais);
decode(decoder);
decoder.endDecoding();
} catch (ContentDecodingException e) {
String reason = e.getMessage();
Log.warning(Log.FAC_NETMANAGER, "Unexpected error decoding ForwardingEntry from bytes. reason: " + reason + "\n");
Log.warningStackTrace(e);
throw new IllegalArgumentException("Unexpected error decoding ForwardingEntry from bytes. reason: " + reason);
}
}
public ForwardingEntry() {
}
public ContentName getPrefixName() { return _prefixName; }
public Integer getFaceID() { return _faceID; }
public void setFaceID(Integer faceID) { _faceID = faceID; }
public String action() { return _action; }
public PublisherPublicKeyDigest getccndId() { return _ccndId; }
public void setccndId(PublisherPublicKeyDigest id) { _ccndId = id; }
/**
*
* @return lifetime of registration in seconds
*/
public Integer getLifetime() { return Integer.valueOf(_lifetime.intValue()); }
public String toFormattedString() {
StringBuilder out = new StringBuilder(256);
if (null != _action) {
out.append("Action: "+ _action + "\n");
} else {
out.append("Action: not present\n");
}
if (null != _faceID) {
out.append("FaceID: "+ _faceID.toString() + "\n");
} else {
out.append("FaceID: not present\n");
}
if (null != _prefixName) {
out.append("Prefix Name: "+ _prefixName + "\n");
} else {
out.append("Prefix Name: not present\n");
}
if (null != _flags) {
out.append("Flags: "+ _flags.toString() + "\n");
} else {
out.append("Flags: not present\n");
}
if (null != _lifetime) {
out.append("Lifetime: "+ _lifetime.toString() + "\n");
} else {
out.append("Lifetime: not present\n");
}
return out.toString();
}
public boolean validateAction(String action) {
if (action != null && action.length() != 0){
if (action.equalsIgnoreCase(ActionType.Register.value()) ||
action.equalsIgnoreCase(ActionType.SelfRegister.value()) ||
action.equalsIgnoreCase(ActionType.UnRegister.value())) {
return true;
}
return false;
}
return true; // Responses don't have actions
}
/**
* Used by NetworkObject to decode the object from a network stream.
* @see org.ccnx.ccn.impl.encoding.XMLEncodable
*/
public void decode(XMLDecoder decoder) throws ContentDecodingException {
decoder.readStartElement(getElementLabel());
if (decoder.peekStartElement(CCNProtocolDTags.Action)) {
_action = decoder.readUTF8Element(CCNProtocolDTags.Action);
}
if (decoder.peekStartElement(CCNProtocolDTags.Name)) {
_prefixName = new ContentName();
_prefixName.decode(decoder) ;
}
if (decoder.peekStartElement(CCNProtocolDTags.PublisherPublicKeyDigest)) {
_ccndId = new PublisherPublicKeyDigest();
_ccndId.decode(decoder);
}
if (decoder.peekStartElement(CCNProtocolDTags.FaceID)) {
_faceID = decoder.readIntegerElement(CCNProtocolDTags.FaceID);
}
if (decoder.peekStartElement(CCNProtocolDTags.ForwardingFlags)) {
_flags = decoder.readIntegerElement(CCNProtocolDTags.ForwardingFlags);
}
if (decoder.peekStartElement(CCNProtocolDTags.FreshnessSeconds)) {
_lifetime = decoder.readIntegerElement(CCNProtocolDTags.FreshnessSeconds);
}
decoder.readEndElement();
}
/**
* Used by NetworkObject to encode the object to a network stream.
* @see org.ccnx.ccn.impl.encoding.XMLEncodable
*/
public void encode(XMLEncoder encoder) throws ContentEncodingException {
if (!validate()) {
throw new ContentEncodingException("Cannot encode " + this.getClass().getName() + ": field values missing.");
}
encoder.writeStartElement(getElementLabel());
if (null != _action && _action.length() != 0)
encoder.writeElement(CCNProtocolDTags.Action, _action);
if (null != _prefixName) {
_prefixName.encode(encoder);
}
if (null != _ccndId) {
_ccndId.encode(encoder);
}
if (null != _faceID) {
encoder.writeElement(CCNProtocolDTags.FaceID, _faceID);
}
if (null != _flags) {
encoder.writeElement(CCNProtocolDTags.ForwardingFlags, _flags);
}
if (null != _lifetime) {
encoder.writeElement(CCNProtocolDTags.FreshnessSeconds, _lifetime);
}
encoder.writeEndElement();
}
@Override
public long getElementLabel() { return CCNProtocolDTags.ForwardingEntry; }
@Override
public boolean validate() {
if (validateAction(_action)){
return true;
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((_action == null) ? 0 : _action.hashCode());
result = prime * result + ((_prefixName == null) ? 0 : _prefixName.hashCode());
result = prime * result + ((_ccndId == null) ? 0 : _ccndId.hashCode());
result = prime * result + ((_faceID == null) ? 0 : _faceID.hashCode());
result = prime * result + ((_flags == null) ? 0 : _flags.hashCode());
result = prime * result + ((_lifetime == null) ? 0 : _lifetime.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
ForwardingEntry other = (ForwardingEntry) obj;
if (_action == null) {
if (other._action != null) return false;
} else if (!_action.equalsIgnoreCase(other._action)) return false;
if (_prefixName == null) {
if (other._prefixName != null) return false;
} else if (!_prefixName.equals(other._prefixName)) return false;
if (_ccndId == null) {
if (other._ccndId != null) return false;
} else if (!_ccndId.equals(other._ccndId)) return false;
if (_faceID == null) {
if (other._faceID != null) return false;
} else if (!_faceID.equals(other._faceID)) return false;
if (_flags == null) {
if (other._flags != null) return false;
} else if (!_flags.equals(other._flags)) return false;
if (_lifetime == null) {
if (other._lifetime != null) return false;
} else if (!_lifetime.equals(other._lifetime)) return false;
return true;
}
} /* ForwardingEntry */
/*************************************************************************************/
/*************************************************************************************/
public PrefixRegistrationManager(CCNHandle handle) throws CCNDaemonException {
super(handle);
}
public PrefixRegistrationManager(CCNNetworkManager networkManager) throws CCNDaemonException {
super(networkManager);
}
public PrefixRegistrationManager() {
}
public void registerPrefix(ContentName prefix, Integer faceID, Integer flags) throws CCNDaemonException {
this.registerPrefix(prefix, null, faceID, flags, Integer.MAX_VALUE);
}
public void registerPrefix(String uri, Integer faceID, Integer flags) throws CCNDaemonException {
this.registerPrefix(uri, null, faceID, flags, Integer.MAX_VALUE);
}
public void registerPrefix(String uri, PublisherPublicKeyDigest publisher, Integer faceID, Integer flags,
Integer lifetime) throws CCNDaemonException {
try {
this.registerPrefix(ContentName.fromURI(uri), null, faceID, flags, Integer.MAX_VALUE);
} catch (MalformedContentNameStringException e) {
String reason = e.getMessage();
String msg = ("MalformedContentName (" + uri + ") , reason: " + reason);
Log.warning(Log.FAC_NETMANAGER, msg);
Log.warningStackTrace(e);
throw new CCNDaemonException(msg);
}
}
public void registerPrefix(ContentName prefixToRegister, PublisherPublicKeyDigest publisher, Integer faceID, Integer flags,
Integer lifetime) throws CCNDaemonException {
if (null == publisher) {
try {
publisher = _manager.getCCNDId();
} catch (IOException e1) {
Log.warning(Log.FAC_NETMANAGER, "Unable to get ccnd id");
Log.warningStackTrace(e1);
throw new CCNDaemonException(e1.getMessage());
}
}
ForwardingEntry forward = new ForwardingEntry(ActionType.Register, prefixToRegister, publisher, faceID, flags, lifetime);
// byte[] entryBits = super.getBinaryEncoding(forward);
/*
* First create a name that looks like 'ccnx:/ccnx/CCNDId/action/ContentObjectWithForwardInIt'
*/
ContentName interestName = null;
try {
interestName = new ContentName(CCNX, _manager.getCCNDId().digest(), ActionType.Register.value());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new CCNDaemonException(e.getMessage());
}
super.sendIt(interestName, forward, null, true);
}
public ForwardingEntry selfRegisterPrefix(String uri) throws CCNDaemonException {
ContentName prefixToRegister;
try {
prefixToRegister = ContentName.fromURI(uri);
} catch (MalformedContentNameStringException e) {
String reason = e.getMessage();
String msg = ("MalformedContentNameStringException for prefix to register (" + uri + ") , reason: " + reason);
Log.warning(Log.FAC_NETMANAGER, msg);
Log.warningStackTrace(e);
throw new CCNDaemonException(msg);
}
return selfRegisterPrefix(prefixToRegister, null, DEFAULT_SELF_REG_FLAGS, Integer.MAX_VALUE);
}
public ForwardingEntry selfRegisterPrefix(ContentName prefixToRegister) throws CCNDaemonException {
return selfRegisterPrefix(prefixToRegister, null, DEFAULT_SELF_REG_FLAGS, Integer.MAX_VALUE);
}
public ForwardingEntry selfRegisterPrefix(ContentName prefixToRegister, Integer faceID) throws CCNDaemonException {
return selfRegisterPrefix(prefixToRegister, faceID, DEFAULT_SELF_REG_FLAGS, Integer.MAX_VALUE);
}
public ForwardingEntry selfRegisterPrefix(ContentName prefixToRegister, Integer faceID, Integer flags) throws CCNDaemonException {
return selfRegisterPrefix(prefixToRegister, faceID, flags, Integer.MAX_VALUE);
}
public ForwardingEntry selfRegisterPrefix(ContentName prefixToRegister, Integer faceID, Integer flags, Integer lifetime) throws CCNDaemonException {
PublisherPublicKeyDigest ccndId;
try {
ccndId = _manager.getCCNDId();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
throw new CCNDaemonException(e1.getMessage());
}
ContentName interestName;
interestName = new ContentName(CCNX, ccndId.digest(), ActionType.SelfRegister.value());
ForwardingEntry forward = new ForwardingEntry(ActionType.SelfRegister, prefixToRegister, ccndId, faceID, flags, lifetime);
byte[] payloadBack = super.sendIt(interestName, forward, null, true);
ForwardingEntry entryBack = new ForwardingEntry(payloadBack);
Log.fine(Log.FAC_NETMANAGER, "registerPrefix: returned {0}", entryBack);
return entryBack;
}
public void unRegisterPrefix(ContentName prefixName, Integer faceID) throws CCNDaemonException {
unRegisterPrefix(prefixName, null, faceID);
}
/**
* Unregister a prefix with ccnd
*
* @param prefixName ContentName of prefix
* @param prefix has callback for completion
* @param faceID faceId that has the prefix registered
* @throws CCNDaemonException
*/
public void unRegisterPrefix(ContentName prefixName, RegisteredPrefix prefix, Integer faceID) throws CCNDaemonException {
PublisherPublicKeyDigest ccndId;
try {
ccndId = _manager.getCCNDId();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
throw new CCNDaemonException(e1.getMessage());
}
ContentName interestName = new ContentName(CCNX, ccndId.digest(), ActionType.UnRegister.value());
ForwardingEntry forward = new ForwardingEntry(ActionType.UnRegister, prefixName, ccndId, faceID, null, null);
super.sendIt(interestName, forward, prefix, false);
}
}