/*
* Part of the CCNx Java Library.
*
* Copyright (C) 2011 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.io.content;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import org.ccnx.ccn.CCNHandle;
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.XMLEncoder;
import org.ccnx.ccn.impl.security.crypto.CCNDigestHelper;
import org.ccnx.ccn.profiles.sync.Sync;
import org.ccnx.ccn.protocol.ContentName;
import static org.ccnx.ccn.impl.CCNFlowControl.SaveType.LOCALREPOSITORY;
import static org.ccnx.ccn.impl.encoding.CCNProtocolDTags.ConfigSlice;
import static org.ccnx.ccn.impl.encoding.CCNProtocolDTags.ConfigSliceList;
import static org.ccnx.ccn.impl.encoding.CCNProtocolDTags.ConfigSliceOp;
import static org.ccnx.ccn.impl.encoding.CCNProtocolDTags.SyncVersion;
/**
* A ConfigSlice describes what names under a particular
* name space will be synchronized. It is always saved
* to the local repository under the localhost namespace.
* They are named by the hash of the contents. This leads
* to slightly different NetworkObject semantics than usual.
*/
public class ConfigSlice extends GenericXMLEncodable {
/**
* Config slice lists require a ConfigSliceOp written before the
* ContentName, although it does nothing. This class
* encodes and decodes the preceding ConfigSliceOp and the
* ContentName together, representing a single filter element.
*/
@SuppressWarnings("serial")
public static class Filter extends ContentName {
public Filter() {
}
public Filter(byte[][] arg) {
super(arg);
}
@Override
public void decode(XMLDecoder decoder) throws ContentDecodingException {
decoder.readIntegerElement(ConfigSliceOp);
super.decode(decoder);
}
@Override
public void encode(XMLEncoder encoder) throws ContentEncodingException {
encoder.writeElement(ConfigSliceOp, 0);
super.encode(encoder);
}
}
private static final int SLICE_VERSION = 20110614;
/**
* A ConfigSlice is always saved in the local repository under
* /localhost/CS.s.cs, so the full name does not need to be passed
* to the constructors here. In addition the object is named after
* a hash of the contents, so this must be available when creating
* a NetworkObject.
*/
public static class NetworkObject extends CCNEncodableObject<ConfigSlice> {
/**
* Read constructor. Use when you have a slice hash (perhaps from enumeration),
* and want to know if it's present or not.
* @param hash of slice data.
* @param handle
* @throws ContentDecodingException
* @throws IOException
*/
public NetworkObject(byte[] hash, CCNHandle handle) throws ContentDecodingException, IOException {
super(ConfigSlice.class, true, nameFromHash(hash), handle);
setSaveType(LOCALREPOSITORY);
}
/**
* Write constructor.
* @param data Used to generate the full object name (which is a hash
* of the data).
* @param handle
* @throws ContentDecodingException
* @throws IOException
*/
public NetworkObject(ConfigSlice data, CCNHandle handle) throws IOException {
super(ConfigSlice.class, false, nameFromHash(data.getHash()), data, LOCALREPOSITORY, handle);
}
/**
* Convenience write constructor.
* Creates an ConfigSlice, calculates the hash and creates a NetworkObject together.
*/
public NetworkObject(ContentName topo, ContentName prefix, Collection<Filter> filters, CCNHandle handle) throws IOException {
this(new ConfigSlice(topo, prefix, filters), handle);
}
public static ContentName nameFromHash(byte[] hash) {
return new ContentName(Sync.SYNC_PREFIX, hash);
}
public ConfigSlice getData() { return _data; }
}
public int version = SLICE_VERSION;
public ContentName topo;
public ContentName prefix;
protected LinkedList<Filter> filters = new LinkedList<Filter>();
public byte[] getHash() {
try {
return CCNDigestHelper.digest(encode());
} catch (ContentEncodingException e) {
// should never happen since we're encoding our own data
throw new RuntimeException(e);
}
}
/**
* Used by NetworkObject read constructor
*/
public ConfigSlice() {
}
public ConfigSlice(ContentName topo, ContentName prefix, Collection<Filter> new_filters) {
this.topo = topo;
this.prefix = prefix;
if (new_filters != null)
filters.addAll(new_filters);
}
@Override
public void decode(XMLDecoder decoder) throws ContentDecodingException {
decoder.readStartElement(getElementLabel());
version = decoder.readIntegerElement(SyncVersion);
topo = new ContentName();
topo.decode(decoder);
prefix = new ContentName();
prefix.decode(decoder);
decoder.readStartElement(ConfigSliceList);
while (decoder.peekStartElement(CCNProtocolDTags.ConfigSliceOp)) {
Filter f = new Filter();
f.decode(decoder);
filters.add(f);
}
decoder.readEndElement();
decoder.readEndElement();
}
@Override
public void encode(XMLEncoder encoder) throws ContentEncodingException {
encoder.writeStartElement(getElementLabel());
encoder.writeElement(SyncVersion, version);
topo.encode(encoder);
prefix.encode(encoder);
encoder.writeStartElement(ConfigSliceList);
for(Filter f : filters)
f.encode(encoder);
encoder.writeEndElement();
encoder.writeEndElement();
}
@Override
public long getElementLabel() {
return ConfigSlice;
}
@Override
public boolean validate() {
return true;
}
/**
* Check that a sync ConfigSlice exists in the local repository, and if not create one.
* @param handle
* @param topo from ConfigSlice
* @param prefix from ConfigSlice
* @param filters from ConfigSlice
* @throws IOException
*/
public static void checkAndCreate(ContentName topo, ContentName prefix, Collection<Filter> filters, CCNHandle handle) throws ContentDecodingException, IOException {
ConfigSlice slice = new ConfigSlice(topo, prefix, filters);
ConfigSlice.NetworkObject csno = new ConfigSlice.NetworkObject(slice.getHash(), handle);
if (!csno.available() || csno.isGone()) {
csno.setData(slice);
csno.save();
}
csno.close();
}
}