    001/*
    002 * Copyright (c) 2007-2017 Xplenty, Inc. All Rights Reserved.
    003 *
    004 * Project and contact information: http://www.cascading.org/
    005 *
    006 * This file is part of the Cascading project.
    007 *
    008 * Licensed under the Apache License, Version 2.0 (the "License");
    009 * you may not use this file except in compliance with the License.
    010 * You may obtain a copy of the License at
    011 *
    012 *     http://www.apache.org/licenses/LICENSE-2.0
    013 *
    014 * Unless required by applicable law or agreed to in writing, software
    015 * distributed under the License is distributed on an "AS IS" BASIS,
    016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    017 * See the License for the specific language governing permissions and
    018 * limitations under the License.
    019 */
    020
    021package cascading.util.cache;
    022
    023import java.util.Collection;
    024import java.util.LinkedHashMap;
    025import java.util.Map;
    026import java.util.Set;
    027
    028import org.slf4j.Logger;
    029import org.slf4j.LoggerFactory;
    030
    031/**
    032 * Implementation of the {@link CascadingCache} interface backed by a {@link java.util.LinkedHashMap}. This implementation
    033 * is used by default by {@link cascading.pipe.assembly.Unique} and {@link cascading.pipe.assembly.AggregateBy} and their
    034 * subclasses.
    035 *
    036 * @see cascading.pipe.assembly.Unique
    037 * @see cascading.pipe.assembly.AggregateBy
    038 * @see DirectMappedCacheFactory
    039 * @see OrderedHashMapCacheFactory
    040 */
    041public class OrderedHashMapCache<Key, Value> implements CascadingCache<Key, Value>
    042  {
    043  /** logger */
    044  private static final Logger LOG = LoggerFactory.getLogger( OrderedHashMapCache.class );
    045
    046  /** capacity of the map. */
    047  private int capacity;
    048
    049  /** call-back used, when entries are removed from the cache. */
    050  private CacheEvictionCallback callback = CacheEvictionCallback.NULL;
    051
    052  /** counts flushes. */
    053  private long flushes = 0;
    054
    055  private LinkedHashMap<Key, Value> backingMap;
    056
    057  @Override
    058  public int getCapacity()
    059    {
    060    return capacity;
    061    }
    062
    063  @Override
    064  public void setCacheEvictionCallback( CacheEvictionCallback cacheEvictionCallback )
    065    {
    066    this.callback = cacheEvictionCallback;
    067    }
    068
    069  @Override
    070  public void setCapacity( int capacity )
    071    {
    072    this.capacity = capacity;
    073    }
    074
    075  @Override
    076  public void initialize()
    077    {
    078    this.backingMap = new LinkedHashMap<Key, Value>( capacity, 0.75f, true )
    079    {
    080    @Override
    081    protected boolean removeEldestEntry( Map.Entry<Key, Value> eldest )
    082      {
    083      boolean doRemove = size() > capacity;
    084      if( doRemove )
    085        {
    086        callback.evict( eldest );
    087        if( flushes % getCapacity() == 0 ) // every multiple, write out data
    088          {
    089          Runtime runtime = Runtime.getRuntime();
    090          long freeMem = runtime.freeMemory() / 1024 / 1024;
    091          long maxMem = runtime.maxMemory() / 1024 / 1024;
    092          long totalMem = runtime.totalMemory() / 1024 / 1024;
    093          LOG.info( "flushed keys num times: {}, with capacity: {}", flushes + 1, capacity );
    094          LOG.info( "mem on flush (mb), free: " + freeMem + ", total: " + totalMem + ", max: " + maxMem );
    095          float percent = (float) totalMem / (float) maxMem;
    096          if( percent < 0.80F )
    097            LOG.info( "total mem is {}% of max mem, to better utilize unused memory consider increasing the cache size", (int) ( percent * 100.0F ) );
    098          }
    099        flushes++;
    100        }
    101      return doRemove;
    102      }
    103    };
    104    }
    105
    106  @Override
    107  public int size()
    108    {
    109    return backingMap.size();
    110    }
    111
    112  @Override
    113  public boolean isEmpty()
    114    {
    115    return backingMap.isEmpty();
    116    }
    117
    118  @Override
    119  public boolean containsKey( Object key )
    120    {
    121    return backingMap.containsKey( key );
    122    }
    123
    124  @Override
    125  public boolean containsValue( Object value )
    126    {
    127    return backingMap.containsValue( value );
    128    }
    129
    130  @Override
    131  public Value get( Object key )
    132    {
    133    return backingMap.get( key );
    134    }
    135
    136  @Override
    137  public Value put( Key key, Value value )
    138    {
    139    return backingMap.put( key, value );
    140    }
    141
    142  @Override
    143  public Value remove( Object key )
    144    {
    145    return backingMap.remove( key );
    146    }
    147
    148  @Override
    149  public void putAll( Map<? extends Key, ? extends Value> m )
    150    {
    151    backingMap.putAll( m );
    152    }
    153
    154  @Override
    155  public void clear()
    156    {
    157    backingMap.clear();
    158    }
    159
    160  @Override
    161  public Set<Key> keySet()
    162    {
    163    return backingMap.keySet();
    164    }
    165
    166  @Override
    167  public Collection<Value> values()
    168    {
    169    return backingMap.values();
    170    }
    171
    172  @Override
    173  public Set<Entry<Key, Value>> entrySet()
    174    {
    175    return backingMap.entrySet();
    176    }
    177  }