/* * Copyright (C) 2000 - 2015 aw2.0 Ltd * * This file is part of 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. * * http://openbd.org/ * https://github.com/OpenBD/openbd-core/blob/master/LICENSE */ package com.naryx.tagfusion.cfm.application.sessionstorage; import java.util.Date; import org.aw20.io.ByteArrayOutputStreamRaw; import org.aw20.io.FileUtil; import org.aw20.security.MD5; import org.bson.Document; import com.bluedragon.mongo.MongoDSN; 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.application.cfApplicationData; import com.naryx.tagfusion.cfm.application.cfSessionData; import com.naryx.tagfusion.cfm.application.sessionUtility; import com.naryx.tagfusion.cfm.application.sessionstorage.SessionStorageFactory.SessionEngine; import com.naryx.tagfusion.cfm.engine.cfEngine; import com.naryx.tagfusion.cfm.engine.cfSession; import com.naryx.tagfusion.cfm.engine.variableStore; public class SessionStorageMongoImpl extends SessionStorageBase implements SessionStorageInterface { private MongoClient mongo = null; private MongoDatabase mdb = null; private MongoCollection<Document> col = null; private boolean indexes = false; private final String uri; /** * mongodb://[a@b:]server1:port1,server2:port2/db * * @param appName * @param connectionUri */ public SessionStorageMongoImpl(String appName, String _connectionUri) throws Exception { super(appName); this.uri = _connectionUri; if ( _connectionUri.startsWith("mongodb://") ){ MongoClientURI clientURI = new MongoClientURI(_connectionUri); mongo = new MongoClient( clientURI ); mdb = mongo.getDatabase( clientURI.getDatabase() == null ? "openbd" : clientURI.getDatabase() ); }else{ // Parse the URL String user = null, pass = null, db = "openbd"; // Are they using a user/pass String connectionUri = _connectionUri.substring( _connectionUri.indexOf("//")+2 ); if ( connectionUri.indexOf("@") != -1 ){ int c1 = connectionUri.indexOf("@"); user = connectionUri.substring( 0, c1 ); int c2 = connectionUri.indexOf(":", c1 + 1 ); if ( c2 == -1 ) throw new Exception("invalid connection uri: " + _connectionUri ); pass = connectionUri.substring( c1 + 1, c2 ); connectionUri = connectionUri.substring( c2+1 ); } // is there a database at the end int c1 = connectionUri.indexOf("/"); if ( c1 != -1 ){ db = connectionUri.substring( c1 + 1 ); connectionUri = connectionUri.substring(0,c1); } mongo = MongoDSN.newClient( connectionUri, user, pass, db ); mdb = mongo.getDatabase( db ); } // Create the collection col = mdb.getCollection( "sessions" ); setIndexes(); cfEngine.log( appName + "; SessionStorageMongo: Created " + _connectionUri ); } private void setIndexes(){ if ( !indexes ){ try{ col.createIndex( new Document("et",true) ); indexes = true; }catch(Exception e){ cfEngine.log( appName + "; Exception: " + e ); } } } public void shutdown(){ if ( mongo != null ){ mongo.close(); mongo = null; mdb = null; col = null; cfEngine.log("SessionStorageMongo: Closed"); } } @Override public boolean onRequestStart(cfSession Session, long sessionTimeOut, sessionUtility sessionInfo) { boolean sessionStart = false; cfSessionData sessData = null; try{ // If the timeout is greater than 0, then lookup it from Mongo if ( sessionTimeOut > 0 ){ setIndexes(); Document keys = new Document( "_id", appName + sessionInfo.getTokenShort() ); Document doc = col.find(keys).first(); if ( doc != null ){ Date et = (Date)doc.get("et"); if ( et.getTime() > System.currentTimeMillis() ){ // Found a live one that we can use!!! Object bufObj = doc.get("d"); 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; } sessData = (cfSessionData)FileUtil.loadClass(buf, true); sessData.setMD5( MD5.getDigest(buf) ); } } } } catch (Exception e) { cfEngine.log( appName + " MongoDBException _id:" + sessionInfo.getTokenShort() + "; Exception: " + e ); } // We don't have a session so create a new one if ( sessData == null ){ sessData = new cfSessionData( appName ); sessionStart = true; } sessData.setSessionID( appName, sessionInfo.CFID, sessionInfo.CFTOKEN ); sessData.setTimeOut( sessionTimeOut ); Session.setQualifiedData( variableStore.SESSION_SCOPE, sessData ); return sessionStart; } /** * Need to page the session out to Mongo * @param session */ public void onRequestEnd(cfSession Session) { cfSessionData sessData = getSessionData( Session ); if ( sessData == null ) return; try{ Document keys = new Document("_id", appName + sessData.getStorageID() ); Document vals = new Document("et", new Date(System.currentTimeMillis() + sessData.getTimeOut() ) ); // Serialize the object ByteArrayOutputStreamRaw bos = new ByteArrayOutputStreamRaw( 32000 ); FileUtil.saveClass(bos, sessData, true); byte[] buf = bos.toByteArray(); // Has it really changed; we only update the last used date if it has if ( !MD5.getDigest(buf).equals( sessData.getMD5() ) ) vals.append("d", buf); col.updateOne( keys, new Document("$set", vals), new UpdateOptions().upsert(true) ); }catch(Exception e){ cfEngine.log( appName + "MongoDBException: " + e ); } } public void onExpireAll(cfApplicationData applicationData) { col.deleteMany( Filters.lte("et", new Date() ) ); } public void onApplicationEnd(cfApplicationData applicationData) { onExpireAll( applicationData ); } public int size() { return (int)col.count(); } @Override public SessionEngine getType() { return SessionStorageFactory.SessionEngine.MONGO; } public String getURI(){ return uri; } }