/* * Part of the CCNx Java Library. * * Copyright (C) 2012, 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.impl.sync; import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.logging.Level; import org.ccnx.ccn.impl.encoding.XMLDecoder; import org.ccnx.ccn.impl.support.Log; import org.ccnx.ccn.io.content.ContentDecodingException; import org.ccnx.ccn.io.content.SyncNodeComposite; import org.ccnx.ccn.protocol.Component; /** * Entry used for navigating trees of hashes. It also does the work of saving and retrieving node data from the common node * cache. But these objects can not be shared across comparators because they contain data about where the comparator is in * a treewalk. * * The policy about how to save nodes is: If the node came from the network, it can be retrieved via a request so * we use a SoftReference to allow these to be garbage collected when necessary. If the node is local, we built it so * can't be guaranteed that we know how to rebuild it if we lose it. So we hold onto these directly here, and remove * them explicitly when no longer needed in the higher level code. */ public class SyncTreeEntry { // Flags values protected final static long COVERED = 1; // Indicates that all names covered by these references have been seen protected final static long LOCAL = 2; // Indicates that the node associated with this entry was created locally protected long _flags; protected byte[] _hash = null; // The hash associated with the node associated with this entry protected SyncNodeComposite _node = null; // If it wasn't created by us, it can be retrieved so saved via SoftReference protected SoftReference<SyncNodeComposite> _softNodeRef = null; protected byte[] _rawContent = null; protected int _position = 0; protected SyncNodeCache _snc = null; public SyncTreeEntry(byte[] hash, SyncNodeCache cache) { _hash = new byte[hash.length]; System.arraycopy(hash, 0, _hash, 0, hash.length); _snc = cache; SyncNodeComposite node = retrieveFromCache(); if (Log.isLoggable(Log.FAC_SYNC, Level.FINEST) && node != null) Log.finest(Log.FAC_SYNC, "Found existing node: {0}", Component.printURI(hash)); } public void setNode(SyncNodeComposite snc) { synchronized (this) { if (getNodeByReference() != null) return; if ((_flags & LOCAL) == 0) { SoftReference<SyncNodeComposite> sr = new SoftReference<SyncNodeComposite>(snc); _softNodeRef = sr; } else _node = snc; _snc.putNode(snc); } if (Log.isLoggable(Log.FAC_SYNC, Level.FINEST)) { SyncNodeComposite.decodeLogging(snc); } } /** * This is used in the case where we have content for a SyncNodeComposite but * don't have its hash until we decode it. * * @param content */ public void setRawContent(byte[] content) { synchronized (this) { if (getNodeIfPossible() != null) return; _rawContent = content; _position = 0; } } /** * Decodes the hash if not yet done. Note that this should not be called from a * handler (unless we already know the node is decoded) because decoding is long and * expensive and could stall the netmanager thread. We allow a separate decoder because * nodes typically contain more elements than are usually allowed by default by the * standard decoder. We can speed things up by preallocating more space. * * @param decoder * @return */ public SyncNodeComposite getNode(XMLDecoder decoder) { SyncNodeComposite node = getNodeIfPossible(); synchronized (this) { if (null != node || null == _rawContent || null == decoder) { if (null != node) _rawContent = null; return node; } // If we have to decode it, its not local by definition node = new SyncNodeComposite(); try { node.decode(_rawContent, decoder); } catch (ContentDecodingException e) { Log.warning("Couldn't decode node {0} due to: {1}", (_hash == null ? "(unknown)" : Component.printURI(_hash)), e.getMessage()); _rawContent = null; return null; } _rawContent = null; _softNodeRef = new SoftReference<SyncNodeComposite>(node); if (null != _snc) { _snc.clearPending(_hash); _snc.putNode(node); } } if (Log.isLoggable(Log.FAC_SYNC, Level.FINEST)) { SyncNodeComposite.decodeLogging(node); } return node; } public SyncNodeComposite getNode() { return getNode(null); } /** * Routines for getting and cycling through the references * @return */ public synchronized SyncNodeComposite.SyncNodeElement getCurrentElement() { SyncNodeComposite node = getNodeIfPossible(); if (null == node) { return null; } return node.getElement(_position); } public synchronized void incPos() { _position++; } public synchronized void setPos(int position) { _position = position; } public synchronized int getPos() { return _position; } public synchronized boolean lastPos() { SyncNodeComposite node = getNodeIfPossible(); if (node == null) return false; // Needed to prompt a getNode return (_position >= node.getRefs().size()); } public synchronized byte[] getHash() { return _hash; } public synchronized boolean isCovered() { return (_flags & COVERED) != 0; } public void setCovered(boolean flag) { setFlag(flag, COVERED); } public void setLocal(boolean flag) { synchronized (this) { if (flag && (_node != null)) return; // Switch to local not allowed } setFlag(flag, LOCAL); } public synchronized boolean isLocal() { return (_flags & LOCAL) != 0; } public boolean equals(Object other) { if (null == other) return false; SyncTreeEntry ste = (SyncTreeEntry)other; return Arrays.equals(_hash, ste.getHash()); } public int hashCode() { return Arrays.hashCode(_hash); } private SyncNodeComposite retrieveFromCache() { SyncNodeComposite snc = _snc.getNode(_hash); if (null != snc) { if (snc.retrievable()) { SoftReference<SyncNodeComposite> sr = new SoftReference<SyncNodeComposite>(snc); _softNodeRef = sr; } else { _node = snc; setFlag(true, LOCAL); } } return snc; } private void setFlag(boolean flag, long type) { synchronized (this) { if (flag) _flags |= type; else _flags &= ~type; } } private SyncNodeComposite getNodeByReference() { synchronized (this) { boolean local = (_flags & LOCAL) != 0; return local ? _node : (null == _softNodeRef ? null : _softNodeRef.get()); } } private SyncNodeComposite getNodeIfPossible() { SyncNodeComposite node = getNodeByReference(); if (null == node) node = retrieveFromCache(); return node; } }