/*
* This program 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.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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.
*
* Copyright (c) 2002-2016 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.platform.plugin.cache;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.util.StringUtil;
import org.pentaho.reporting.libraries.xmlns.parser.Base64;
import java.io.Serializable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Eviction strategy that kills old cache when accessed
*/
public class DeleteOldOnAccessCache extends AbstractReportContentCache {
private static final Log logger = LogFactory.getLog( DeleteOldOnAccessCache.class );
private static final String SEGMENT = "long_term";
public static final String TIMESTAMP = "timestamp";
public static final int MILLIS_IN_DAY = 86400000;
public static final String ANONYMOUS = "anonymous";
private long millisToLive;
public DeleteOldOnAccessCache( final ICacheBackend backend ) {
super( backend );
}
public DeleteOldOnAccessCache() {
}
public void setDaysToLive( final long daysToLive ) {
this.millisToLive = MILLIS_IN_DAY * daysToLive;
}
/*for testing purposes*/
protected void setMillisToLive( final long millisToLive ) {
this.millisToLive = millisToLive;
}
@Override protected List<String> computeKey( final String key ) {
final IPentahoSession session = PentahoSessionHolder.getSession();
//Don't use username explicitly - compute hash
return Collections.unmodifiableList( Arrays.asList( SEGMENT, createKey( session.getName() ), key ) );
}
/**
* Cleans old entries Saves value with timestamp
*
* @param key key
* @param value value
* @return success
*/
@Override public boolean put( final String key, final IReportContent value ) {
return put( key, value, new HashMap<>() );
}
/**
* Cleans old entries Saves value with timestamp
*
* @param key key
* @param value value
* @param metaData metaData
* @return success
*/
@Override public boolean put( final String key, final IReportContent value, Map<String, Serializable> metaData ) {
cleanUp();
metaData.put( TIMESTAMP, System.currentTimeMillis() );
getBackend().write( computeKey( key ), value, metaData );
return false;
}
/**
* @param key key
* @return ReportContent
*/
@Override public IReportContent get( final String key ) {
cleanUp();
return super.get( key );
}
/**
* @param key key
* @return Map<String, Serializable>
*/
@Override public Map<String, Serializable> getMetaData( String key ) {
cleanUp();
return super.getMetaData( key );
}
/**
* Cleans old files
*/
@Override public void cleanup() {
cleanUp();
}
@Override public void cleanupCurrentSession() {
final IPentahoSession session = PentahoSessionHolder.getSession();
final List<String> key = Collections.unmodifiableList( Arrays.asList( SEGMENT, createKey( session.getName() ) ) );
final ICacheBackend backend = getBackend();
backend.purgeSegment( key, ( k, m ) -> true );
}
private void cleanUp() {
logger.debug( "Starting periodical cache eviction" );
final long currentTimeMillis = System.currentTimeMillis();
final ICacheBackend backend = getBackend();
backend.purgeSegment( Collections.singletonList( SEGMENT ),
new BiPredicate<List<String>, Map<String, Serializable>>() {
@Override public boolean test( final List<String> key, final Map<String, Serializable> md ) {
final Object o = md.get( TIMESTAMP );
if ( o instanceof Long ) {
final long timestamp = (Long) o;
if ( currentTimeMillis - timestamp > millisToLive ) {
logger.debug( "Purged long-term cache: " + key );
return true;
}
}
return false;
}
} );
logger.debug( "Finished periodical cache eviction" );
}
private String createKey( final String key ) {
if ( StringUtil.isEmpty( key ) ) {
return ANONYMOUS;
}
try {
final MessageDigest md = MessageDigest.getInstance( "SHA-256" );
md.update( key.getBytes() );
final byte[] digest = md.digest();
return new String( Base64.encode( digest ) );
} catch ( final NoSuchAlgorithmException e ) {
throw new Error( e );
}
}
}