/* * Copyright (C) 2012-2015 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/ * server user password db collection */ package com.naryx.tagfusion.cfm.cache.impl; import java.io.IOException; import java.util.Date; import org.aw20.io.ByteArrayOutputStreamRaw; import org.aw20.io.FileUtil; import org.aw20.util.SystemClock; import org.aw20.util.SystemClockEvent; import org.bson.Document; import ucar.unidata.util.DateUtil; import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.Filters; import com.mongodb.client.model.UpdateOptions; 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.cfStringData; import com.naryx.tagfusion.cfm.engine.cfStructData; import com.naryx.tagfusion.cfm.engine.dataNotSupportedException; public class MongoCacheImpl implements CacheInterface, SystemClockEvent { private cfStructData props; private MongoClient mongo = null; private MongoDatabase db = null; private MongoCollection<Document> col = null; @Override public cfStructData getProperties() { return props; } @Override public void setProperties(String region, cfStructData _props) throws Exception { shutdown(); this.props = _props; if ( !props.containsKey("mongoclienturi") ) throw new Exception("'mongoclienturi' does not exist. in format: mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]"); if ( !props.containsKey("db") ) props.setData("db", "openbdcache" ); if ( !props.containsKey("collection") ) props.setData("collection", region ); MongoClientURI mcu = new MongoClientURI(props.getData("mongoclienturi").getString()); mongo = new MongoClient( mcu ); db = mongo.getDatabase( props.getData("db").getString() ); createCollection(); SystemClock.setListenerMinute( this, 5 ); } private void createCollection() throws dataNotSupportedException{ col = db.getCollection( props.getData("collection").getString() ); col.createIndex( new Document("id",true) ); col.createIndex( new Document("ct",true) ); } @Override public void set(String id, cfData data, long ageMs, long idleTime) { //TODO: idleTime not implemented so idleSpan won't work Date ct = null; statsSet++; if ( ageMs == 0 ){ delete( id, false ); return; } else if ( ageMs < 0 ){ ct = new Date( System.currentTimeMillis() + DateUtil.MILLIS_MONTH ); } else ct = new Date( System.currentTimeMillis() + ageMs ); // Set up the key Document vals = new Document("id", id).append("ct", ct); if ( data instanceof cfStringData ) vals.append( "vs", ((cfStringData)data).getString() ); else{ ByteArrayOutputStreamRaw bos = new ByteArrayOutputStreamRaw( 32000 ); try { FileUtil.saveClass(bos, data, true); vals.append( "vb", bos.toByteArray() ); } catch (IOException e) { cfEngine.log( getName() + " id:" + id + "; Exception: " + e ); return; } } Document keys = new Document("id", id); col.updateOne(keys, new Document("$set", vals), new UpdateOptions().upsert(true)); } @Override public cfData get(String id) { Document doc = col.find( Filters.eq("id", id) ).first(); if ( doc != null ){ // Check that the date is correct Date ct = (Date)doc.get("ct"); if ( ct.getTime() < System.currentTimeMillis() ){ delete( id, true ); statsMissAge++; return null; } statsHit++; if ( doc.containsKey("vs") ) return new cfStringData( (String)doc.get("vs") ); else{ Object bufObj = doc.get("vb"); byte[] buf; if ( bufObj instanceof org.bson.types.Binary ){ buf = ( (org.bson.types.Binary) bufObj ).getData(); }else{ // should be byte []. Keep for backwards compatibility buf = (byte[]) bufObj; } try { return (cfData)FileUtil.loadClass(buf, true); } catch (Exception e) { cfEngine.log( getName() + " id:" + id + "; Exception: " + e ); } } }else{ statsMiss++; } return null; } @Override public void delete(String id, boolean exact) { statsDelete++; if ( exact ){ col.deleteOne( new Document("id", id) ); }else{ col.deleteMany( Filters.regex("id", "" + id + "*") ); } } @Override public void deleteAll() { col.drop(); try { createCollection(); } catch (Exception e) { cfEngine.log( getName() + " " + e.getMessage() ); } } @Override public String getName() { return "MongoCache"; } @Override public cfArrayData getAllIds() { return cfArrayData.createArray(1); } private int statsSet = 0, statsGet = 0, statsDelete = 0, statsMiss = 0, statsHit = 0, statsMissAge = 0; @Override public cfStructData getStats() { cfStructData stats = new cfStructData(); stats.setData("set", statsSet); stats.setData("get", statsGet); stats.setData("hits", statsHit); stats.setData("miss", statsMiss); stats.setData("missage", statsMissAge); stats.setData("delete", statsDelete); if ( col != null ){ stats.setData("size", col.count() ); stats.setData("collection", col.getNamespace().getCollectionName() ); stats.setData("db", db.getName() ); } return stats; } @Override public void shutdown() { SystemClock.removeListenerMinute( this ); if ( mongo != null ){ mongo.close(); mongo = null; db = null; col = null; } } @Override public void clockEvent(int eventType) { col.deleteMany( Filters.lte("ct", new Date() ) ); } }