/*
* Copyright (C) 2012 TagServlet Ltd
*
* This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
*
* OpenBD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* OpenBD 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBD. If not, see http://www.gnu.org/licenses/
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with any of the JARS listed in the README.txt (or a modified version of
* (that library), containing parts covered by the terms of that JAR, the
* licensors of this Program grant you additional permission to convey the
* resulting work.
* README.txt @ http://www.openbluedragon.org/license/README.txt
*
* http://openbd.org/
* $Id: MemcachedCacheImpl.java 2431 2014-03-30 20:00:05Z alan $
*/
package com.naryx.tagfusion.cfm.cache.impl;
import java.net.SocketAddress;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import net.spy.memcached.AddrUtil;
import net.spy.memcached.BinaryConnectionFactory;
import net.spy.memcached.MemcachedClient;
import org.aw20.util.StringUtil;
import com.naryx.tagfusion.cfm.cache.CacheInterface;
import com.naryx.tagfusion.cfm.engine.cfArrayData;
import com.naryx.tagfusion.cfm.engine.cfData;
import com.naryx.tagfusion.cfm.engine.cfEngine;
import com.naryx.tagfusion.cfm.engine.cfJavaObjectData;
import com.naryx.tagfusion.cfm.engine.cfStringData;
import com.naryx.tagfusion.cfm.engine.cfStructData;
public class MemcachedCacheImpl implements CacheInterface {
private static long MONTH_MS = 30l * 24l * 60l * 60l * 1000l;
private MemcachedClient memcache = null;
private int waitTimeSeconds = 0;
private cfStructData props;
@Override
public void setProperties(String region, cfStructData _props) throws Exception {
this.props = _props;
if ( memcache != null )
shutdown();
if ( !props.containsKey("server") )
throw new Exception("'server' does not exist. in format: server1:port1 server2:port2");
if ( !props.containsKey("waittimeseconds") )
props.setData("waittimeseconds", 5);
waitTimeSeconds = StringUtil.toInteger( props.getData("waittimeseconds").getInt(), 5 );
String server = props.getData("server").getString();
memcache = new MemcachedClient( new BinaryConnectionFactory(), AddrUtil.getAddresses(server) );
cfEngine.log( getName() + " server: " + server + "; WaitTimeSeconds: " + waitTimeSeconds );
}
@Override
public void set(String id, cfData data, long ageMS, long idleTime) {
int ageSecs = 0;
if ( ageMS > MONTH_MS ){
ageSecs = (int)(MONTH_MS / 1000) - 60;
}else if ( ageMS < MONTH_MS && ageMS > 0 ){
ageSecs = (int)(ageMS / 1000);
}else if ( ageMS < 0 )
ageSecs = (int)(MONTH_MS / 1000) - 60;
Future<Boolean> status = null;
try{
if ( data instanceof cfStringData )
status = memcache.set( id, ageSecs, ((cfStringData)data).getString() );
else
status = memcache.set( id, ageSecs, data );
status.get(waitTimeSeconds, TimeUnit.SECONDS);
}catch(Exception e){
cfEngine.log( getName() + " set Failed: " + e.getMessage() );
status.cancel(false);
}
}
@Override
public cfData get(String id) {
Future<Object> future = memcache.asyncGet(id);
try {
Object obj = future.get(waitTimeSeconds, TimeUnit.SECONDS);
if ( obj == null )
return null;
else if ( obj instanceof String )
return new cfStringData( (String)obj );
else if ( obj instanceof cfData )
return (cfData)obj;
else
return new cfJavaObjectData( obj );
} catch (Exception e) {
cfEngine.log( getName() + " get Failed: " + e.getMessage() );
future.cancel(false);
}
return null;
}
@Override
public void delete(String id, boolean exact) {
Future<Boolean> success = memcache.delete(id);
try {
success.get();
} catch (Exception e) {
cfEngine.log( getName() + " deleteFailed: " + e.getMessage() );
success.cancel(false);
}
}
@Override
public void deleteAll() {
Future<Boolean> success = memcache.flush();
try {
success.get();
} catch (Exception e) {
cfEngine.log( getName() + " flushFailed: " + e.getMessage() );
success.cancel(false);
}
}
@Override
public String getName() {
return "memcached";
}
@Override
public cfArrayData getAllIds() {
return cfArrayData.createArray(1);
}
@Override
public cfStructData getStats() {
cfStructData stats = new cfStructData();
Map<SocketAddress, Map<String, String>> ms = memcache.getStats();
Iterator<SocketAddress> it = ms.keySet().iterator();
while ( it.hasNext() ){
SocketAddress sa = it.next();
cfStructData sd = new cfStructData();
Map<String,String> innerMap = ms.get(sa);
Iterator<String> innerMapIt = innerMap.keySet().iterator();
String key;
while ( innerMapIt.hasNext() ){
key = innerMapIt.next();
sd.setData( key, innerMap.get(key) );
}
stats.setData( sa.toString(), sd );
}
return stats;
}
@Override
public cfStructData getProperties() {
return props;
}
@Override
public void shutdown() {
memcache.shutdown();
memcache = null;
}
}