/*
* Part of the CCNx Java Library.
*
* Copyright (C) 2010, 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.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import org.ccnx.ccn.CCNHandle;
import org.ccnx.ccn.impl.CCNFlowControl.SaveType;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.io.content.Link.LinkObject;
import org.ccnx.ccn.profiles.repo.RepositoryControl;
import org.ccnx.ccn.protocol.CCNTime;
import org.ccnx.ccn.protocol.ContentName;
import org.ccnx.ccn.protocol.KeyLocator;
import org.ccnx.ccn.protocol.PublisherPublicKeyDigest;
/**
* Wrapper for a generic network object that requests a local repository to hold
* a copy of the object whenever it is updated or saved. A local repository
* is one connected directly to the same ccnd as the application; it may have
* a distinguished role as the repository that is always available
* for local configuration data regardless of external connectivity. If there
* is more than one repository that is local, the behavior is undefined.
*
* To use this wrapper, create a network object instance as usual and then instantiate
* the wrapper with the network object. Use the wrapper whenever possible instead of the
* underlying network object. To access distinctive features of the particular network object
* subclass, call the object() method on the wrapper to get the underlying object.
*
* @author jthornton
*
*/
public class LocalCopyWrapper implements UpdateListener {
final CCNNetworkObject<?> _netobj;
final CCNHandle _handle;
final ConcurrentHashMap<UpdateListener, UpdateListener> _updateListeners = new ConcurrentHashMap<UpdateListener, UpdateListener>(1);
public LocalCopyWrapper(CCNNetworkObject<?> obj) throws IOException {
_netobj = obj;
_handle = _netobj.getHandle();
_netobj.addListener(this);
// Check for data already available, before we had a chance to add listener
if (_netobj.isSaved()) {
localCopy();
}
}
public CCNNetworkObject<?> object() {
return _netobj;
}
protected void localCopy() {
try {
RepositoryControl.localRepoSync(_handle, _netobj);
} catch (IOException e) {
if (Log.isLoggable(Level.INFO)) {
Log.info("Local repo sync failed for network object: " + e.getMessage());
}
}
}
public void addListener(UpdateListener listener) {
_updateListeners.put(listener, listener);
}
public void removeListener(UpdateListener listener) {
_updateListeners.remove(listener);
}
public void clearListeners() {
_updateListeners.clear();
}
public boolean available() {
return _netobj.available();
}
public boolean isSaved() throws IOException {
return _netobj.isSaved();
}
public boolean hasError() {
return _netobj.hasError();
}
public IOException getError() {
return _netobj.getError();
}
public void clearError() {
_netobj.clearError();
}
public boolean contentEquals(Object obj) {
return _netobj.contentEquals(obj);
}
public byte [] getContentDigest() throws IOException {
return _netobj.getContentDigest();
}
public void close() {
_netobj.removeListener(this);
_netobj.close();
}
public void disableFlowControl() {
_netobj.disableFlowControl();
}
@Override
public int hashCode() {
return _netobj.hashCode();
}
public boolean equals(Object obj) {
if( null == obj )
return false;
if (getClass() != obj.getClass()) {
return false;
}
LocalCopyWrapper other = (LocalCopyWrapper)obj;
return _netobj.equals(other._netobj);
}
public Long firstSegmentNumber() {
return _netobj.firstSegmentNumber();
}
public ContentName getBaseName() {
return _netobj.getBaseName();
}
public PublisherPublicKeyDigest getContentPublisher() throws IOException {
return _netobj.getContentPublisher();
}
public LinkObject getDereferencedLink() {
return _netobj.getDereferencedLink();
}
public byte[] getFirstDigest() {
return _netobj.getFirstDigest();
}
public KeyLocator getPublisherKeyLocator() throws IOException {
return _netobj.getPublisherKeyLocator();
}
public CCNTime getVersion() throws IOException {
return _netobj.getVersion();
}
public byte [] getVersionComponent() throws IOException {
return _netobj.getVersionComponent();
}
public ContentName getVersionedName() {
return _netobj.getVersionedName();
}
public boolean isGone() {
return _netobj.isGone();
}
public boolean save() throws ContentEncodingException, IOException {
boolean result = _netobj.save();
if (result) localCopy();
return result;
}
public boolean save(CCNTime version) throws ContentEncodingException, IOException {
boolean result = _netobj.save(version);
if (result) localCopy();
return result;
}
public boolean saveAsGone() throws ContentEncodingException, IOException {
boolean result = _netobj.saveAsGone();
if (result) localCopy();
return result;
}
public SaveType saveType() { return _netobj.saveType(); }
public void setOurPublisherInformation(PublisherPublicKeyDigest publisherIdentity, KeyLocator keyLocator) {
_netobj.setOurPublisherInformation(publisherIdentity, keyLocator);
}
public boolean update() throws ContentDecodingException, IOException {
return _netobj.update();
}
public boolean update(ContentName name, PublisherPublicKeyDigest publisher) throws ContentDecodingException, IOException {
return _netobj.update(name, publisher);
}
public boolean update(long timeout) throws ContentDecodingException, IOException {
return _netobj.update(timeout);
}
public void updateInBackground() throws IOException {
_netobj.updateInBackground();
}
public void updateInBackground(boolean continuousUpdates) throws IOException {
_netobj.updateInBackground(continuousUpdates);
}
public void updateInBackground(boolean continuousUpdates, UpdateListener listener) throws IOException {
addListener(listener);
_netobj.updateInBackground(continuousUpdates);
}
public void updateInBackground(ContentName latestVersionKnown, boolean continuousUpdates) throws IOException {
_netobj.updateInBackground(latestVersionKnown, continuousUpdates);
}
public void updateInBackground(ContentName latestVersionKnown, boolean continuousUpdates, UpdateListener listener) throws IOException {
addListener(listener);
_netobj.updateInBackground(latestVersionKnown, continuousUpdates);
}
public void waitForData() {
_netobj.waitForData();
}
public void waitForData(long timeout) {
_netobj.waitForData(timeout);
}
public void newVersionAvailable(CCNNetworkObject<?> newVersion, boolean wasSave) {
// We probably want to make a local copy regardless, as the save might have been raw,
// or not hit our local repository.
localCopy();
// any registered listeners
// keySet() is weakly consistent and will reflect the state of the ConcurerntHashMap
// at the time keySet is called.
for (UpdateListener listener : _updateListeners.keySet()) {
listener.newVersionAvailable(newVersion, wasSave);
}
}
}