/* * Part of the CCNx Java Library. * * Copyright (C) 2008, 2009, 2011, 2012 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.repo; import java.io.IOException; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Level; import org.ccnx.ccn.config.SystemConfiguration; import org.ccnx.ccn.impl.InterestTable; import org.ccnx.ccn.impl.InterestTable.Entry; import org.ccnx.ccn.impl.support.Log; import org.ccnx.ccn.profiles.nameenum.NameEnumerationResponse; import org.ccnx.ccn.protocol.ContentName; import org.ccnx.ccn.protocol.ContentObject; import org.ccnx.ccn.protocol.Interest; /** * So the main listener can output interests sooner, we do the data store work * in a separate thread. */ public class RepositoryDataHandler implements Runnable { public static final int THROTTLE_TOP = 2000; public static final int THROTTLE_BOTTOM = 1800; private final RepositoryServer _server; private final Queue<ContentObject> _queue = new ConcurrentLinkedQueue<ContentObject>(); private final InterestTable<ContentName> _pendingKeyChecks = new InterestTable<ContentName>(); private boolean _shutdown = false; private boolean _shutdownComplete = false; protected int _currentQueueSize; protected boolean _throttled = false; public RepositoryDataHandler(RepositoryServer server) { _server = server; } public void add(ContentObject co) { _currentQueueSize++; if (!_throttled && _currentQueueSize > THROTTLE_TOP) { _throttled = true; _server.setThrottle(true); } _queue.add(co); _queue.notify(); } public void addKeyCheck(ContentName target) { _pendingKeyChecks.add(new Interest(target), target); } /** * The content listener runs this thread to store data using the content store. * The thread also checks for policy updates which may reset the repository's * namespace and sends "early" nameEnumerationResponses when requested by the * store. * * @see RepositoryStore */ public void run() { while (!_shutdownComplete) { ContentObject co = null; do { co = _queue.poll(); if (null == co) { if (_shutdown) { synchronized (this) { _shutdownComplete = true; notifyAll(); } return; } try { _queue.wait(SystemConfiguration.MEDIUM_TIMEOUT); } catch (InterruptedException e) {} } } while (null == co); _currentQueueSize--; if (_throttled && _currentQueueSize < THROTTLE_BOTTOM) { _throttled = false; _server.setThrottle(false); } try { if (Log.isLoggable(Log.FAC_REPO, Level.FINER)) { Log.finer(Log.FAC_REPO, "Saving content in: " + co.toString()); } NameEnumerationResponse ner = _server.getRepository().saveContent(co); if (!_shutdown) { if (ner!=null && ner.hasNames()) { _server.sendEnumerationResponse(ner); } } // When a write or some syncs are first requested we don't know what key data // was being used because this is in the ContentObject which of course we didn't // have yet. Bbut we need this data to make sure the key is saved along with the file. // Now we can find the key data and check if we have it already or need to get it // too. Also the key locator that we dont have yet could have been a link. We // didn't know that either. If it was we have to get the data it points to. // // Also we have to check for more locators associated with our new object // and the objects pointed to by the links. Entry<ContentName> entry = _pendingKeyChecks.removeMatch(co); if (null != entry) { ContentName nameToCheck = entry.value(); if (Log.isLoggable(Log.FAC_REPO, Level.FINER)) { Log.finer(Log.FAC_REPO, "Processing key check entry: {0}", nameToCheck); } ContentName linkCheck = _server.getLinkedKeyTarget(co); if (null != linkCheck) { if (Log.isLoggable(Log.FAC_REPO, Level.FINER)) { Log.finer(Log.FAC_REPO, "Processing key check entry for link: {0}", linkCheck); } Interest linkInterest = new Interest(linkCheck); _server.doSync(linkInterest, linkInterest); syncKeysForObject(co, linkCheck); } syncKeysForObject(co, nameToCheck); } } catch (Exception e) { e.printStackTrace(); Log.logStackTrace(Level.WARNING, e); } } } private void syncKeysForObject(ContentObject co, ContentName name) throws RepositoryException, IOException { ContentName target = _server.getKeyTargetFromObject(co, name); if (null != target) { if (Log.isLoggable(Log.FAC_REPO, Level.FINER)) { Log.finer(Log.FAC_REPO, "Fetching key from dataHandler: " + target); } Interest interest = Interest.constructInterest(target, _server.getExcludes(), 1, 3, null, null); addKeyCheck(target); _server.doSync(interest, interest); } } public void shutdown() { _shutdown = true; synchronized (this) { while (!_shutdownComplete) { try { wait(SystemConfiguration.LONG_TIMEOUT); } catch (InterruptedException e) {} } } } public int getCurrentQueueSize() { return _currentQueueSize; } }