001/*
002 * Copyright (c) 2007-2015 Concurrent, 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  }