/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.support; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; import java.util.Collection; import freenet.client.FetchException; import freenet.client.FetchResult; import freenet.client.HighLevelSimpleClient; import freenet.client.InsertException; import freenet.client.async.BaseClientPutter; import freenet.client.async.ClientContext; import freenet.client.async.ClientGetCallback; import freenet.client.async.ClientGetter; import freenet.client.async.ClientPutCallback; import freenet.keys.FreenetURI; import freenet.node.Node; import freenet.node.PrioRunnable; import freenet.support.io.TempBucketFactory; /** * A thread which periodically wakes up and iterates to start fetches and/or inserts. * * When calling <code>start()</code>, the thread will iterate the first time after <code>getStartupDelay()</code> milliseconds. * After each iteration, it will sleep for <code>getSleepTime()</code> milliseconds. * * @deprecated * This class uses {@link TrivialTicker} in a bogus way. * See <a href="https://bugs.freenetproject.org/view.php?id=6423">issue 6423</a>.<br> * Also, it was not used by fred itself but only by Web Of Trust and Freetalk, while not being * part of the fred official plugin API. Thus it should not be contained in fred.<br> * If you do continue to need this class for a plugin, please move it to a shared library for * plugins.<br> * In that case, please use the version of Web Of Trust as it contains fixes for the existing * bugs of this version here. * @author xor */ @Deprecated public abstract class TransferThread implements PrioRunnable, ClientGetCallback, ClientPutCallback { private final String mName; protected final Node mNode; protected final HighLevelSimpleClient mClient; protected final ClientContext mClientContext; protected final TempBucketFactory mTBF; private TrivialTicker mTicker; private final Collection<ClientGetter> mFetches = createFetchStorage(); private final Collection<BaseClientPutter> mInserts = createInsertStorage(); public TransferThread(Node myNode, HighLevelSimpleClient myClient, String myName) { mNode = myNode; mClient = myClient; mClientContext = mNode.clientCore.clientContext; mTBF = mNode.clientCore.tempBucketFactory; mName = myName; mTicker = new TrivialTicker(mNode.executor); } /** * Tells this TransferThread to start it's execution. You have to call this after constructing an object of an implementing class - it must not * be called in the constructors of implementing classes. */ public void start() { Logger.debug(this, "Starting..."); mTicker.queueTimedJob(this, mName, getStartupDelay(), false, true); } /** Specify the priority of this thread. Priorities to return can be found in class NativeThread. */ @Override public abstract int getPriority(); @Override public void run() { long sleepTime = SECONDS.toMillis(1); try { Logger.debug(this, "Loop running..."); iterate(); sleepTime = getSleepTime(); } catch(Exception e) { Logger.error(this, "Error in iterate() or getSleepTime() probably", e); } finally { Logger.debug(this, "Loop finished. Sleeping for " + MINUTES.convert(sleepTime, MILLISECONDS) + " minutes."); mTicker.queueTimedJob(this, mName, sleepTime, false, true); } } /** * Wakes up the thread so that iterate() is called. */ public void nextIteration() { mTicker.rescheduleTimedJob(this, mName, 0); } protected void abortAllTransfers() { Logger.debug(this, "Trying to stop all fetches & inserts..."); abortFetches(); abortInserts(); } protected void abortFetches() { Logger.debug(this, "Trying to stop all fetches..."); if(mFetches != null) synchronized(mFetches) { ClientGetter[] fetches = mFetches.toArray(new ClientGetter[mFetches.size()]); int fcounter = 0; for(ClientGetter fetch : fetches) { /* This calls onFailure which removes the fetch from mFetches on the same thread, therefore we need to copy to an array */ fetch.cancel(mNode.clientCore.clientContext); ++fcounter; } Logger.debug(this, "Stopped " + fcounter + " current fetches."); } } protected void abortInserts() { Logger.debug(this, "Trying to stop all inserts..."); if(mInserts != null) synchronized(mInserts) { BaseClientPutter[] inserts = mInserts.toArray(new BaseClientPutter[mInserts.size()]); int icounter = 0; for(BaseClientPutter insert : inserts) { /* This calls onFailure which removes the fetch from mFetches on the same thread, therefore we need to copy to an array */ insert.cancel(mNode.clientCore.clientContext); ++icounter; } Logger.debug(this, "Stopped " + icounter + " current inserts."); } } protected void addFetch(ClientGetter g) { synchronized(mFetches) { mFetches.add(g); } } protected void removeFetch(ClientGetter g) { synchronized(mFetches) { mFetches.remove(g); } Logger.debug(this, "Removed request for " + g.getURI()); } protected void addInsert(BaseClientPutter p) { synchronized(mInserts) { mInserts.add(p); } } protected void removeInsert(BaseClientPutter p) { synchronized(mInserts) { mInserts.remove(p); } Logger.debug(this, "Removed insert for " + p.getURI()); } protected int fetchCount() { synchronized(mFetches) { return mFetches.size(); } } protected int insertCount() { synchronized(mInserts) { return mInserts.size(); } } public void terminate() { Logger.debug(this, "Terminating..."); mTicker.shutdown(); try { abortAllTransfers(); } catch(RuntimeException e) { Logger.error(this, "Aborting all transfers failed", e); } Logger.debug(this, "Terminated."); } protected abstract Collection<ClientGetter> createFetchStorage(); protected abstract Collection<BaseClientPutter> createInsertStorage(); protected abstract long getStartupDelay(); protected abstract long getSleepTime(); /** * Called by the TransferThread after getStartupDelay() milliseconds for the first time and then after each getSleepTime() milliseconds. */ protected abstract void iterate(); /* Fetches */ /** * You have to do "finally { removeFetch() }" when using this function. */ @Override public abstract void onSuccess(FetchResult result, ClientGetter state); /** * You have to do "finally { removeFetch() }" when using this function. */ @Override public abstract void onFailure(FetchException e, ClientGetter state); /* Inserts */ /** * You have to do "finally { removeInsert() }" when using this function. */ @Override public abstract void onSuccess(BaseClientPutter state); /** * You have to do "finally { removeInsert() }" when using this function. */ @Override public abstract void onFailure(InsertException e, BaseClientPutter state); @Override public abstract void onFetchable(BaseClientPutter state); @Override public abstract void onGeneratedURI(FreenetURI uri, BaseClientPutter state); }