Package net.tomp2p.dht

Source Code of net.tomp2p.dht.StorageMemory

/*
* Copyright 2012 Thomas Bocek
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package net.tomp2p.dht;

import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;

import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number320;
import net.tomp2p.peers.Number480;
import net.tomp2p.peers.Number640;
import net.tomp2p.storage.Data;
import net.tomp2p.storage.Storage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StorageMemory implements Storage {

  public static final int DEFAULT_STORAGE_CHECK_INTERVAL= 60 * 1000;
  public static final int DEFAULT_MAX_VERSIONS= -1;

    private static final Logger LOG = LoggerFactory.getLogger(StorageMemory.class);
   

    // Core
    final private NavigableMap<Number640, Data> dataMap = new ConcurrentSkipListMap<Number640, Data>();

    // Maintenance
    final private Map<Number640, Long> timeoutMap = new ConcurrentHashMap<Number640, Long>();
    final private ConcurrentSkipListMap<Long, Set<Number640>> timeoutMapRev = new ConcurrentSkipListMap<Long, Set<Number640>>();

    // Protection
    final private Map<Number320, PublicKey> protectedMap = new ConcurrentHashMap<Number320, PublicKey>();
    final private Map<Number480, PublicKey> entryMap = new ConcurrentHashMap<Number480, PublicKey>();

    // Responsibility
    final private Map<Number160, Set<Number160>> responsibilityMap = new ConcurrentHashMap<Number160, Set<Number160>>();
    final private Map<Number160, Set<Number160>> responsibilityMapRev = new ConcurrentHashMap<Number160, Set<Number160>>();
   
    final int storageCheckIntervalMillis;
    final int maxVersions;
   
    public StorageMemory() {
      this(DEFAULT_STORAGE_CHECK_INTERVAL, DEFAULT_MAX_VERSIONS);
    }
   
    public StorageMemory(int storageCheckIntervalMillis) {
      this(storageCheckIntervalMillis, DEFAULT_MAX_VERSIONS);
    }

    public StorageMemory(int storageCheckIntervalMillis, int maxVersions) {
      this.storageCheckIntervalMillis = storageCheckIntervalMillis;
    this.maxVersions = maxVersions;
  }

  // Core
    @Override
    public boolean put(Number640 key, Data value) {
        dataMap.put(key, value);
        if (maxVersions > 0) {
          NavigableMap<Number640, Data> versions = dataMap.subMap(
        new Number640(key.locationKey(), key.domainKey(), key.contentKey(), Number160.ZERO), true,
        new Number640(key.locationKey(), key.domainKey(), key.contentKey(), Number160.MAX_VALUE), true);
     
      while (!versions.isEmpty()
              && versions.firstKey().versionKey().timestamp() + maxVersions <= versions.lastKey().versionKey()
                      .timestamp()) {
        Map.Entry<Number640, Data> entry = versions.pollFirstEntry();
        remove(entry.getKey(), false);
        removeTimeout(entry.getKey());
      }
        }
        return true;
    }

    @Override
    public Data get(Number640 key) {
        return dataMap.get(key);
    }

    @Override
    public boolean contains(Number640 key) {
        return dataMap.containsKey(key);
    }

    @Override
    public int contains(Number640 fromKey, Number640 toKey) {
        NavigableMap<Number640, Data> tmp = dataMap.subMap(fromKey, true, toKey, true);
        return tmp.size();
    }

    @Override
    public Data remove(Number640 key, boolean returnData) {
      return dataMap.remove(key);
    }

    @Override
    public NavigableMap<Number640, Data> remove(Number640 fromKey, Number640 toKey, boolean returnData) {
        NavigableMap<Number640, Data> tmp = dataMap.subMap(fromKey, true, toKey, true);
       
        // new TreeMap<Number640, Data>(tmp); is not possible as this may lead to no such element exception:
        //
        //      java.util.NoSuchElementException: null
        //      at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapIter.advance(ConcurrentSkipListMap.java:3030) ~[na:1.7.0_60]
        //      at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3100) ~[na:1.7.0_60]
        //      at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3096) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2394) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2344) ~[na:1.7.0_60]
        //      at java.util.TreeMap.<init>(TreeMap.java:195) ~[na:1.7.0_60]
        //      at net.tomp2p.dht.StorageMemory.subMap(StorageMemory.java:119) ~[classes/:na]
        //
        // the reason is that the size in TreeMap.buildFromSorted is stored beforehand, then iteratated. If the size changes,
        // then you will call next() that returns null and an exception is thrown.
        final NavigableMap<Number640, Data> retVal = new TreeMap<Number640, Data>();
        for(Map.Entry<Number640, Data> entry:tmp.entrySet()) {
          retVal.put(entry.getKey(), entry.getValue());
        }
       
        tmp.clear();
        return retVal;
    }

    @Override
    public NavigableMap<Number640, Data> subMap(Number640 fromKey, Number640 toKey, int limit,
            boolean ascending) {
      final NavigableMap<Number640, Data> tmp = dataMap.subMap(fromKey, true, toKey, true);
        final NavigableMap<Number640, Data> retVal = new TreeMap<Number640, Data>();
        if (limit < 0) {
         
          // new TreeMap<Number640, Data>(tmp); is not possible as this may lead to no such element exception:
            //
            //      java.util.NoSuchElementException: null
            //      at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapIter.advance(ConcurrentSkipListMap.java:3030) ~[na:1.7.0_60]
            //      at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3100) ~[na:1.7.0_60]
            //      at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3096) ~[na:1.7.0_60]
            //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2394) ~[na:1.7.0_60]
            //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
            //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
            //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
            //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
            //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
            //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
            //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
            //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2344) ~[na:1.7.0_60]
            //      at java.util.TreeMap.<init>(TreeMap.java:195) ~[na:1.7.0_60]
            //      at net.tomp2p.dht.StorageMemory.subMap(StorageMemory.java:119) ~[classes/:na]
            //
            // the reason is that the size in TreeMap.buildFromSorted is stored beforehand, then iteratated. If the size changes,
            // then you will call next() that returns null and an exception is thrown.
          for(Map.Entry<Number640, Data> entry:(ascending ? tmp : tmp.descendingMap()).entrySet()) {
              retVal.put(entry.getKey(), entry.getValue());
            }
        } else {
            limit = Math.min(limit, tmp.size());
            Iterator<Map.Entry<Number640, Data>> iterator = ascending ? tmp.entrySet().iterator() : tmp
                    .descendingMap().entrySet().iterator();
            for (int i = 0; iterator.hasNext() && i < limit; i++) {
                Map.Entry<Number640, Data> entry = iterator.next();
                retVal.put(entry.getKey(), entry.getValue());
            }
        }
        return retVal;
    }

    @Override
    public NavigableMap<Number640, Data> map() {
     
      // new TreeMap<Number640, Data>(tmp); is not possible as this may lead to no such element exception:
        //
        //      java.util.NoSuchElementException: null
        //      at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapIter.advance(ConcurrentSkipListMap.java:3030) ~[na:1.7.0_60]
        //      at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3100) ~[na:1.7.0_60]
        //      at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3096) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2394) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
        //      at java.util.TreeMap.buildFromSorted(TreeMap.java:2344) ~[na:1.7.0_60]
        //      at java.util.TreeMap.<init>(TreeMap.java:195) ~[na:1.7.0_60]
        //      at net.tomp2p.dht.StorageMemory.subMap(StorageMemory.java:119) ~[classes/:na]
        //
        // the reason is that the size in TreeMap.buildFromSorted is stored beforehand, then iteratated. If the size changes,
        // then you will call next() that returns null and an exception is thrown.
        final NavigableMap<Number640, Data> retVal = new TreeMap<Number640, Data>();
        for(final Map.Entry<Number640, Data> entry:dataMap.entrySet()) {
          retVal.put(entry.getKey(), entry.getValue());
        }
     
        return retVal;
    }

    // Maintenance
    @Override
    public void addTimeout(Number640 key, long expiration) {
        Long oldExpiration = timeoutMap.put(key, expiration);
        Set<Number640> tmp = putIfAbsent2(expiration,
                Collections.newSetFromMap(new ConcurrentHashMap<Number640, Boolean>()));
        tmp.add(key);
        if (oldExpiration == null) {
            return;
        }
        removeRevTimeout(key, oldExpiration);
    }

    @Override
    public void removeTimeout(Number640 key) {
        Long expiration = timeoutMap.remove(key);
        if (expiration == null) {
            return;
        }
        removeRevTimeout(key, expiration);
    }

    private void removeRevTimeout(Number640 key, Long expiration) {
        Set<Number640> tmp = timeoutMapRev.get(expiration);
        if (tmp != null) {
            tmp.remove(key);
            if (tmp.isEmpty()) {
                timeoutMapRev.remove(expiration);
            }
        }
    }

    @Override
    public Collection<Number640> subMapTimeout(long to) {
        SortedMap<Long, Set<Number640>> tmp = timeoutMapRev.subMap(0L, to);
        Collection<Number640> toRemove = new ArrayList<Number640>();
        for (Set<Number640> set : tmp.values()) {
            toRemove.addAll(set);
        }
        return toRemove;
    }

    // Protection
    @Override
    public boolean protectDomain(Number320 key, PublicKey publicKey) {
        protectedMap.put(key, publicKey);
        return true;
    }

    @Override
    public boolean isDomainProtectedByOthers(Number320 key, PublicKey publicKey) {
        PublicKey other = protectedMap.get(key);
        if (other == null) {
            return false;
        }
        return !other.equals(publicKey);
    }

    private Set<Number640> putIfAbsent2(long expiration, Set<Number640> hashSet) {
        Set<Number640> timeouts = timeoutMapRev.putIfAbsent(expiration, hashSet);
        return timeouts == null ? hashSet : timeouts;
    }

  @Override
  public Collection<Number160> findPeerIDsForResponsibleContent(Number160 locationKey) {
    return responsibilityMap.get(locationKey);
  }

    @Override
    public Collection<Number160> findContentForResponsiblePeerID(Number160 peerID) {
    return responsibilityMapRev.get(peerID);
    }

  @Override
  public boolean updateResponsibilities(Number160 locationKey, Number160 peerId) {
    Set<Number160> peerIDs = putIfAbsent(locationKey, new HashSet<Number160>());
    boolean isNew = peerIDs.add(peerId);
    Set<Number160> contentIDs = putIfAbsentRev(peerId, new HashSet<Number160>());
    contentIDs.add(locationKey);
    if (isNew && LOG.isDebugEnabled()) {
      LOG.debug("Update {} is responsible for key {}.", peerId, locationKey);
    }
    return isNew;
  }

    private Set<Number160> putIfAbsent(Number160 locationKey, Set<Number160> hashSet) {
        Set<Number160> peerIDs = ((ConcurrentMap<Number160, Set<Number160>>) responsibilityMap).putIfAbsent(
            locationKey, hashSet);
        return peerIDs == null ? hashSet : peerIDs;
    }

  private Set<Number160> putIfAbsentRev(Number160 peerId, Set<Number160> hashSet) {
    Set<Number160> contentIDs = ((ConcurrentMap<Number160, Set<Number160>>) responsibilityMapRev)
        .putIfAbsent(peerId, hashSet);
    return contentIDs == null ? hashSet : contentIDs;
  }

    @Override
    public void removeResponsibility(Number160 locationKey) {
       Set<Number160> peerIds = responsibilityMap.remove(locationKey);
       if(peerIds != null) {
      for (Number160 peerId : peerIds) {
        removeRevResponsibility(peerId, locationKey);
      }
      LOG.debug("Remove responsiblity for {}.", locationKey);
       }
    }
   
    private void removeRevResponsibility(Number160 peerId, Number160 locationKey) {
        Set<Number160> contentIDs = responsibilityMapRev.get(peerId);
        if (contentIDs != null) {
            contentIDs.remove(locationKey);
            if (contentIDs.isEmpty()) {
                responsibilityMapRev.remove(peerId);
            }
        }
    }

    @Override
  public void removeResponsibility(Number160 locationKey, Number160 peerId) {
    Set<Number160> peerIds = responsibilityMap.get(locationKey);
    if (peerIds != null && peerIds.remove(peerId)) {
      removeRevResponsibility(peerId, locationKey);
      LOG.debug("Remove responsibility of {} for {}.", peerId, locationKey);
    }
  }

    // Misc
    @Override
    public void close() {
        dataMap.clear();
        protectedMap.clear();
        timeoutMap.clear();
        timeoutMapRev.clear();
    }

  @Override
    public boolean protectEntry(Number480 key, PublicKey publicKey) {
    entryMap.put(key, publicKey);
      return true;
    }

  @Override
    public boolean isEntryProtectedByOthers(Number480 key, PublicKey publicKey) {
    PublicKey other = entryMap.get(key);
        if (other == null) {
            return false;
        }
        return !other.equals(publicKey);
    }

  @Override
    public int storageCheckIntervalMillis() {
      return storageCheckIntervalMillis;
    }
}
TOP

Related Classes of net.tomp2p.dht.StorageMemory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.