/*
* A CCNx library test.
*
* Copyright (C) 2011, 2013 Palo Alto Research Center, Inc.
*
* This work is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 as published by the
* Free Software Foundation.
* This work 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 General Public License
* for more details. You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
package org.ccnx.ccn.test;
import java.io.IOException;
import java.util.ArrayList;
import org.ccnx.ccn.CCNContentHandler;
import org.ccnx.ccn.CCNHandle;
import org.ccnx.ccn.CCNInterestHandler;
import org.ccnx.ccn.KeyManager;
import org.ccnx.ccn.config.ConfigurationException;
import org.ccnx.ccn.config.SystemConfiguration;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.protocol.ContentName;
import org.ccnx.ccn.protocol.ContentObject;
import org.ccnx.ccn.protocol.Interest;
/**
* This class is designed to handle actually erroring in assertions that fail within CCN handlers. Normally
* since the handler is called by a different thread than the test, an assertion failure within the handler
* would not actually cause the test to fail.
*
* To use this test, replace CCNHandle with an AssertionCCNHandle. Then after each expressInterest or registerFilter
* call within your test you must call checkError to insure that the handler ran without error.
*/
public class AssertionCCNHandle extends CCNHandle {
protected Error _error = null;
protected boolean _callbackSeen = false;
protected ArrayList<RelatedInterestHandler> _contentHandlers = new ArrayList<RelatedInterestHandler>();
protected ArrayList<RelatedFilterListener> _interestHandlers = new ArrayList<RelatedFilterListener>();
protected class RelatedInterestHandler {
AssertionContentHandler _aHandler;
CCNContentHandler _handler;
protected RelatedInterestHandler(AssertionContentHandler aListener, CCNContentHandler handler) {
_aHandler = aListener;
_handler = handler;
}
}
protected class RelatedFilterListener {
AssertionInterestHandler _aHandler;
CCNInterestHandler _handler;
protected RelatedFilterListener(AssertionInterestHandler aListener, CCNInterestHandler handler) {
_aHandler = aListener;
_handler = handler;
}
}
protected AssertionCCNHandle() throws ConfigurationException, IOException {
super();
}
protected AssertionCCNHandle(KeyManager keyManager) throws IOException {
super(keyManager);
}
public static AssertionCCNHandle open() throws ConfigurationException, IOException {
try {
return new AssertionCCNHandle();
} catch (ConfigurationException e) {
Log.severe(Log.FAC_NETMANAGER, "Configuration exception initializing CCN library: " + e.getMessage());
throw e;
} catch (IOException e) {
Log.severe(Log.FAC_NETMANAGER, "IO exception initializing CCN library: " + e.getMessage());
throw e;
}
}
public static AssertionCCNHandle open(KeyManager keyManager) throws IOException {
return new AssertionCCNHandle(keyManager);
}
/**
* Overrides of CCNHandle calls referencing the listener
*/
public void expressInterest(
Interest interest,
CCNContentHandler handler) throws IOException {
AssertionContentHandler ail = null;
synchronized (_contentHandlers) {
ail = getInterestListener(handler);
if (null == ail) {
ail = new AssertionContentHandler(handler);
_contentHandlers.add(new RelatedInterestHandler(ail, handler));
}
ail._references++;
}
super.expressInterest(interest, ail);
}
public void cancelInterest(Interest interest, CCNContentHandler handler) {
AssertionContentHandler toCancel = null;
synchronized (_contentHandlers) {
toCancel = getInterestListener(handler);
if (null == toCancel) {
Log.warning("Questionable cancel of never expressed interest: %0", interest);
toCancel = new AssertionContentHandler(handler);
_contentHandlers.add(new RelatedInterestHandler(toCancel, handler));
}
}
super.cancelInterest(interest, toCancel);
synchronized (_contentHandlers) {
if (--toCancel._references <= 0)
_contentHandlers.remove(new RelatedInterestHandler(toCancel, handler));
}
}
public void registerFilter(ContentName filter,
CCNInterestHandler handler) throws IOException {
AssertionInterestHandler afh = null;
synchronized (_interestHandlers) {
afh = getInterestHandler(handler);
if (null == afh) {
afh = new AssertionInterestHandler(handler);
_interestHandlers.add(new RelatedFilterListener(afh, handler));
}
afh._references++;
}
super.registerFilter(filter, afh);
}
public void unregisterFilter(ContentName filter, CCNInterestHandler handler) {
AssertionInterestHandler toUnregister = null;
synchronized (_interestHandlers) {
toUnregister = getInterestHandler(handler);
if (null == toUnregister) {
Log.warning("Questionable unregister of never registered filter: %0", filter);
toUnregister = new AssertionInterestHandler(handler);
_interestHandlers.add(new RelatedFilterListener(toUnregister, handler));
}
}
super.unregisterFilter(filter, toUnregister);
synchronized (_interestHandlers) {
if (--toUnregister._references <= 0)
_interestHandlers.remove(new RelatedFilterListener(toUnregister, handler));
}
}
/**
* Should be called after any callback has been triggered on the handle that would have
* received the callback
* @param timeout millis to wait for callback to occur - doesn't wait if NO_TIMEOUT is used
* @throws Error
* @throws InterruptedException
*/
public void checkError(long timeout) throws Error, InterruptedException {
if (timeout > 0 || timeout == SystemConfiguration.NO_TIMEOUT) {
synchronized (this) {
long startTime = System.currentTimeMillis();
while (!_callbackSeen) {
if (timeout == SystemConfiguration.NO_TIMEOUT)
wait();
else {
wait(timeout);
if ((System.currentTimeMillis() - startTime) > timeout)
break;
}
}
_callbackSeen = false;
}
}
if (null != _error)
throw _error;
}
private AssertionContentHandler getInterestListener(CCNContentHandler handler) {
for (RelatedInterestHandler ril : _contentHandlers) {
if (ril._handler == handler) {
return ril._aHandler;
}
}
return null;
}
private AssertionInterestHandler getInterestHandler(CCNInterestHandler handler) {
for (RelatedFilterListener rfl : _interestHandlers) {
if (rfl._handler == handler) {
return rfl._aHandler;
}
}
return null;
}
protected class AssertionInterestHandler implements CCNInterestHandler {
protected CCNInterestHandler _handler;
protected int _references = 0;
public AssertionInterestHandler(CCNInterestHandler handler) {
_handler = handler;
}
public boolean handleInterest(Interest interest) {
boolean result = false;
_callbackSeen = true;
try {
result = _handler.handleInterest(interest);
} catch (Error e) {
_error = e;
}
synchronized (this) {
notifyAll();
}
if (null != _error)
throw _error;
return result;
}
}
protected class AssertionContentHandler implements CCNContentHandler {
protected CCNContentHandler _handler;
protected int _references = 0;
public AssertionContentHandler(CCNContentHandler handler) {
_handler = handler;
}
public Interest handleContent(ContentObject data, Interest interest) {
Interest result = null;
_callbackSeen = true;
try {
result = _handler.handleContent(data, interest);
} catch (Error e) {
_error = e;
}
synchronized (this) {
notifyAll();
}
if (null != _error)
throw _error;
return result;
}
}
}