/* * Part of the CCNx Java Library. * * Copyright (C) 2008-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.impl.repo; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.logging.Level; import org.ccnx.ccn.CCNHandle; import org.ccnx.ccn.impl.CCNFlowControl; import org.ccnx.ccn.impl.CCNFlowControl.SaveType; import org.ccnx.ccn.impl.encoding.CCNProtocolDTags; import org.ccnx.ccn.impl.encoding.GenericXMLEncodable; 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.ErrorStateException; import org.ccnx.ccn.io.content.CCNEncodableObject; import org.ccnx.ccn.io.content.ContentDecodingException; import org.ccnx.ccn.io.content.ContentEncodingException; import org.ccnx.ccn.io.content.ContentGoneException; import org.ccnx.ccn.io.content.ContentNotReadyException; import org.ccnx.ccn.protocol.ContentName; import org.ccnx.ccn.protocol.ContentObject; import org.ccnx.ccn.protocol.KeyLocator; import org.ccnx.ccn.protocol.MalformedContentNameStringException; import org.ccnx.ccn.protocol.PublisherPublicKeyDigest; /** * Object to return information associated with a Repository to a repository client */ public class RepositoryInfo extends GenericXMLEncodable implements XMLEncodable{ protected static double _version = 1.1; protected String _repoVersion = null; protected String _localName = null; protected GlobalPrefix _globalPrefix = null; protected ArrayList<ContentName> _names = new ArrayList<ContentName>(); protected ContentName _policyName; protected String _infoString = null; protected RepoInfoType _type = RepoInfoType.INFO; /** * The two possible types. * INFO is used for most acknowledgements * DATA is used for positive responses (no need for data transfer) to checked write requests */ public enum RepoInfoType { INFO ("INFO"), DATA ("DATA"); private String _stringValue = null; RepoInfoType() {} RepoInfoType(String stringValue) { this._stringValue = stringValue; } static RepoInfoType valueFromString(String value) { for (RepoInfoType pv : RepoInfoType.values()) { if (pv._stringValue != null) { if (pv._stringValue.equals(value.toUpperCase())) return pv; } } return null; } } private static class GlobalPrefix extends ContentName { private static final long serialVersionUID = -6669511721290965466L; public GlobalPrefix(ContentName cn) { super(cn); } public GlobalPrefix() { super(); } @Override public long getElementLabel() { return CCNProtocolDTags.GlobalPrefixName; } } protected static final HashMap<RepoInfoType, String> _InfoTypeNames = new HashMap<RepoInfoType, String>(); /** * A CCNNetworkObject wrapper around RepositoryInfo, used for easily saving and retrieving * versioned RepositoryInfos to CCN. A typical pattern for using network objects to save * objects that happen to be encodable or serializable is to incorporate such a static * member wrapper class subclassing CCNEncodableObject, CCNSerializableObject, or * CCNNetworkObject itself inside the main class definition. */ public static class RepositoryInfoObject extends CCNEncodableObject<RepositoryInfo> { /** * Read constructor. We have separated constructors into "write" and "read"; * basically if you are only going to read with an object, use a read constructor * unless you do not want update() to be called to read data in the constructor * (for example, because you know no data has been written yet to this name). * In this case, use a write constructor, set the data argument to null, and * later call update or one of the forms of updateInBackground to read data * into the object. * * If the first thing you are going to do with an object is write data, use a * write constructor, with a SaveType chosen to reflect where you are going to * write data to (e.g. directly to the network or to a repository). The SaveType * just controls the choice of flow controller used by the object, so constructors * that explicitly specify a flow controller do not need a SaveType. * * If you want to use an object to both read and write data, and you create it * with a read constructor, you must call setupSave on the object prior to writing * to set its SaveType, unless you specified a flow controller in the constructor. */ public RepositoryInfoObject(ContentName name, CCNHandle handle) throws ContentDecodingException, IOException { super(RepositoryInfo.class, true, name, (PublisherPublicKeyDigest)null, handle); } /** * Read constructor. */ public RepositoryInfoObject(ContentName name, PublisherPublicKeyDigest publisher, CCNHandle handle) throws ContentDecodingException, IOException { super(RepositoryInfo.class, true, name, publisher, handle); } /** * Read constructor. */ public RepositoryInfoObject(ContentName name, PublisherPublicKeyDigest publisher, CCNFlowControl flowControl) throws ContentDecodingException, IOException { super(RepositoryInfo.class, true, name, publisher, flowControl); } /** * Read constructor. */ public RepositoryInfoObject(ContentObject firstBlock, CCNHandle handle) throws ContentDecodingException, IOException { super(RepositoryInfo.class, true, firstBlock, handle); } /** * Read constructor. */ public RepositoryInfoObject(ContentObject firstBlock, CCNFlowControl flowControl) throws ContentDecodingException, IOException { super(RepositoryInfo.class, true, firstBlock, flowControl); } /** * Write constructor. */ public RepositoryInfoObject(ContentName name, RepositoryInfo data, SaveType saveType, CCNHandle handle) throws IOException { super(RepositoryInfo.class, true, name, data, saveType, handle); } /** * Write constructor. */ public RepositoryInfoObject(ContentName name, RepositoryInfo data, SaveType saveType, PublisherPublicKeyDigest publisher, KeyLocator keyLocator, CCNHandle handle) throws IOException { super(RepositoryInfo.class, true, name, data, saveType, publisher, keyLocator, handle); } /** * Write constructor. */ public RepositoryInfoObject(ContentName name, RepositoryInfo data, PublisherPublicKeyDigest publisher, KeyLocator keyLocator, CCNFlowControl flowControl) throws IOException { super(RepositoryInfo.class, true, name, data, publisher, keyLocator, flowControl); } public RepositoryInfo repositoryInfo() throws ContentNotReadyException, ContentGoneException, ErrorStateException { return data(); } } /** * The main constructor used to create the information returned by a repository during initialization * * @param version * @param globalPrefix * @param localName * @throws MalformedContentNameStringException */ public RepositoryInfo(String version, String globalPrefix, String localName) throws MalformedContentNameStringException { _localName = localName; _repoVersion = version; if (!globalPrefix.startsWith("/")) globalPrefix = "/" + _globalPrefix; _globalPrefix = new GlobalPrefix(ContentName.fromNative(globalPrefix)); } /** * Create String info to return * * @param version * @param globalPrefix * @param localName * @param info - a string with info to return * @throws MalformedContentNameStringException */ public RepositoryInfo(String version, ContentName globalPrefix, String localName, String info) throws MalformedContentNameStringException { _localName = localName; _repoVersion = version; _infoString = info; _globalPrefix = new GlobalPrefix(globalPrefix); } /** * Currently used only to test decoding/encoding * * @param version * @param globalPrefix * @param localName * @param names * @throws MalformedContentNameStringException */ public RepositoryInfo(String version, String globalPrefix, String localName, ArrayList<ContentName> names) throws MalformedContentNameStringException { this(localName, globalPrefix, version); for (ContentName name : names) { _names.add(name); } _type = RepoInfoType.DATA; } /** * Constructor for a DATA packet using names * * @param version * @param globalPrefix * @param localName * @param names * @throws MalformedContentNameStringException */ public RepositoryInfo(String version, ContentName globalPrefix, String localName, ArrayList<ContentName> names) throws MalformedContentNameStringException { _localName = localName; _repoVersion = version; _globalPrefix = new GlobalPrefix(globalPrefix); for (ContentName name : names) { _names.add(name); } _type = RepoInfoType.DATA; } public RepositoryInfo() {} // For decoding /** * Gets the current local name as a slash separated String * @return the local name */ public String getLocalName() { return _localName; } /** * Gets the current global prefix as a ContentName * @return the prefix */ public ContentName getGlobalPrefix() { return _globalPrefix; } /** * Gets the ContentName that would be used to change policy for this repository * @return the name */ public synchronized ContentName getPolicyName() { if (null == _policyName) { _policyName = BasicPolicy.getPolicyName(_globalPrefix); if (Log.isLoggable(Log.FAC_REPO, Level.INFO)) { Log.info(Log.FAC_REPO, "REPO: Policy name for repository: {0}", _policyName); } } return _policyName; } /** * Gets String info returned by the repo * @return */ public String getInfo() { return _infoString; } /** * Get the type of information encapsulated in this object (INFO or DATA). * @return */ public RepoInfoType getType() { return _type; } /** * Gets the repository store version as a String * @return the version */ public String getRepositoryVersion() { return _repoVersion; } /** * Get the repository store version as a Double * @return the version */ public String getVersion() { return Double.toString(_version); } @Override public void decode(XMLDecoder decoder) throws ContentDecodingException { decoder.readStartElement(getElementLabel()); _version = Double.valueOf(decoder.readUTF8Element(CCNProtocolDTags.Version)); _type = RepoInfoType.valueFromString(decoder.readUTF8Element(CCNProtocolDTags.Type)); _repoVersion = decoder.readUTF8Element(CCNProtocolDTags.RepositoryVersion); _globalPrefix = new GlobalPrefix(); _globalPrefix.decode(decoder); _localName = decoder.readUTF8Element(CCNProtocolDTags.LocalName); while (decoder.peekStartElement(CCNProtocolDTags.Name)) { ContentName name = new ContentName(); name.decode(decoder); _names.add(name); } if (decoder.peekStartElement(CCNProtocolDTags.InfoString)) _infoString = decoder.readUTF8Element(CCNProtocolDTags.InfoString); decoder.readEndElement(); } @Override public void encode(XMLEncoder encoder) throws ContentEncodingException { if (!validate()) { throw new ContentEncodingException("Cannot encode " + this.getClass().getName() + ": field values missing."); } encoder.writeStartElement(getElementLabel()); encoder.writeElement(CCNProtocolDTags.Version, Double.toString(_version)); encoder.writeElement(CCNProtocolDTags.Type, getType().toString()); encoder.writeElement(CCNProtocolDTags.RepositoryVersion, _repoVersion); _globalPrefix.encode(encoder); encoder.writeElement(CCNProtocolDTags.LocalName, _localName); if (_names.size() > 0) { for (ContentName name : _names) name.encode(encoder); } if (null != _infoString) encoder.writeElement(CCNProtocolDTags.InfoString, _infoString); encoder.writeEndElement(); } @Override public long getElementLabel() { return CCNProtocolDTags.RepositoryInfo; } @Override public boolean validate() { return true; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((_globalPrefix == null) ? 0 : _globalPrefix.hashCode()); result = prime * result + ((_localName == null) ? 0 : _localName.hashCode()); result = prime * result + ((_names == null) ? 0 : _names.hashCode()); result = prime * result + ((_policyName == null) ? 0 : _policyName.hashCode()); result = prime * result + ((_repoVersion == null) ? 0 : _repoVersion.hashCode()); result = prime * result + ((_type == null) ? 0 : _type.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; RepositoryInfo other = (RepositoryInfo) obj; if (_globalPrefix == null) { if (other._globalPrefix != null) return false; } else if (!_globalPrefix.equals(other._globalPrefix)) return false; if (_localName == null) { if (other._localName != null) return false; } else if (!_localName.equals(other._localName)) return false; if (_names == null) { if (other._names != null) return false; } else if (!_names.equals(other._names)) return false; if (_policyName == null) { if (other._policyName != null) return false; } else if (!_policyName.equals(other._policyName)) return false; if (_repoVersion == null) { if (other._repoVersion != null) return false; } else if (!_repoVersion.equals(other._repoVersion)) return false; if (_type == null) { if (other._type != null) return false; } else if (!_type.equals(other._type)) return false; if (_infoString == null) { if (other._infoString != null) return false; } else if (!_infoString.equals(other._infoString)) return false; return true; } }