/* * Part of the CCNx Java Library. * * Copyright (C) 2011-2013 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 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; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import org.ccnx.ccn.CCNHandle; import org.ccnx.ccn.config.SystemConfiguration; 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.impl.support.Log; import org.ccnx.ccn.profiles.sync.Sync; import org.ccnx.ccn.protocol.ContentName; public class ConfigSlice extends GenericXMLEncodable { public int version = Sync.SLICE_VERSION; public ContentName topo; public ContentName prefix; protected LinkedList<Filter> filters = new LinkedList<Filter>(); /** * 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(ContentName cn) { super(cn); } 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); } } 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); } /** * 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 ConfigSlice 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); ConfigSliceObject csno = new ConfigSliceObject(slice, handle); boolean updated = csno.update(SystemConfiguration.SHORT_TIMEOUT); if (updated) Log.fine(Log.FAC_SYNC, "found this slice in my repo! {0}", csno.getVersionedName()); else Log.fine(Log.FAC_SYNC, "didn't find a slice in my repo."); if (!updated || (updated && (!csno.available() || csno.isGone()))) { Log.fine(Log.FAC_SYNC, "need to save my data to create the slice for the repo!"); csno.setData(slice); csno.save(); } else { Log.fine(Log.FAC_SYNC, "don't need to do anything... returning the existing slice"); } csno.close(); return slice; } public void checkAndCreate(CCNHandle handle) throws ContentDecodingException, ContentEncodingException, IOException{ ConfigSliceObject existingSlice; try { //existingSlice = new ConfigSlice.NetworkObject(this.getHash(), handle); existingSlice = new ConfigSliceObject(this, handle); boolean updated = existingSlice.update(SystemConfiguration.SHORT_TIMEOUT); if (!updated || (updated && (!existingSlice.available() || existingSlice.isGone()))) { existingSlice.setData(this); existingSlice.save(); } } catch (ContentDecodingException e) { Log.warning(Log.FAC_REPO, "ContentDecodingException: Unable to read in existing slice data from repository."); throw e; } catch (IOException e) { Log.warning(Log.FAC_REPO, "IOException: error when attempting to retrieve existing slice"); throw e; } existingSlice.close(); } public boolean deleteSlice(CCNHandle handle) throws IOException{ ConfigSliceObject existingSlice; try { existingSlice = new ConfigSliceObject(this.getHash(), handle); return existingSlice.saveAsGone(); } catch (ContentDecodingException e) { Log.warning(Log.FAC_REPO, "ContentDecodingException: Unable to read in existing slice data from repository."); throw new IOException("Unable to delete slice from repository: " + e.getMessage()); } catch (IOException e) { Log.warning(Log.FAC_REPO, "IOException: error when attempting to retrieve existing slice before deletion"); throw new IOException("Unable to delete slice from repository: " + e.getMessage()); } } 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); } } @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; } public int hashCode() { return Arrays.hashCode(getHash()); } public boolean equals(Object obj) { if (null == obj) return false; if (! (obj instanceof ConfigSlice)) return false; ConfigSlice otherSlice = (ConfigSlice)obj; return Arrays.equals(this.getHash(), otherSlice.getHash()); } }