/*
* Copyright (C) 2000 - 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://www.openbluedragon.org/
*
* $Id: SalesForceQueryCallback.java 2131 2012-06-27 19:02:26Z alan $
*/
package org.alanwilliamson.openbd.plugin.salesforce;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.bluedragon.plugin.ObjectCFC;
import com.bluedragon.plugin.PluginManager;
import com.naryx.tagfusion.cfm.engine.cfArgStructData;
import com.naryx.tagfusion.cfm.engine.cfArrayData;
import com.naryx.tagfusion.cfm.engine.cfComponentData;
import com.naryx.tagfusion.cfm.engine.cfData;
import com.naryx.tagfusion.cfm.engine.cfNumberData;
import com.naryx.tagfusion.cfm.engine.cfSession;
import com.naryx.tagfusion.cfm.engine.cfmRunTimeException;
import com.naryx.tagfusion.cfm.sql.cfSQLQueryData;
import com.naryx.tagfusion.cfm.tag.tagUtils;
import com.sforce.soap.partner.PartnerConnection;
import com.sforce.soap.partner.QueryResult;
import com.sforce.soap.partner.sobject.SObject;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.bind.XmlObject;
public class SalesForceQueryCallback extends SalesForceBaseFunction {
private static final long serialVersionUID = 1L;
public SalesForceQueryCallback(){
min = 3; max = 5;
setNamedParams( new String[]{ "email", "passwordtoken", "query", "cfc", "timeout" } );
}
public String[] getParamInfo(){
return new String[]{
"SalesFoce Email address",
"SalesForce password and token concatenated together",
"SalesForce SOQL statement",
"The CFC object that will have the callback performed on it on the method: onSalesForceQueryResult( qry, recordsin, recordstotal ). If this function returns false; then it will stop further processing",
"the time in milliseconds, that the connection will wait for a response"
};
}
public java.util.Map getInfo(){
return makeInfo(
"salesforce-plugin",
"Executes a query against SalesForce, sending the results to a call back CFC for each page of results that come back",
ReturnType.NUMERIC );
}
public cfData execute(cfSession _session, cfArgStructData argStruct) throws cfmRunTimeException {
String query = getNamedStringParam(argStruct, "query", null );
if ( query == null )
throwException( _session, "query was not properly defined" );
cfData cfcdata = getNamedParam(argStruct, "cfc", null);
if ( cfcdata == null || !(cfcdata instanceof cfComponentData) )
throwException( _session, "cfc callback was not properly defined" );
cfComponentData cfcCallback = (cfComponentData)cfcdata;
// Run the query
try {
long startTime = System.currentTimeMillis();
int total = getQuery(_session, argStruct, query, cfcCallback );
PluginManager.getPlugInManager().log( "SalesForceQueryCallback() Time=" + (System.currentTimeMillis()-startTime) + "ms; rows=" + total + "; " + query );
return new cfNumberData( total );
}catch(Exception e){
PluginManager.getPlugInManager().log( "SalesForceQueryCallback().Exception:" + e.getMessage() );
throwException( _session, e.getMessage() );
}
return null;
}
/**
* Executes the query
*
* @param _session
* @param argStruct
* @param query
* @return
* @throws ConnectionException
* @throws cfmRunTimeException
*/
private int getQuery(cfSession _session, cfArgStructData argStruct, String query, cfComponentData cfcCallback) throws cfmRunTimeException{
PartnerConnection connection = null;
try{
connection = getConnection( _session, argStruct );
long time = System.currentTimeMillis();
int total = runQuery(_session, connection, query, cfcCallback );
time = System.currentTimeMillis() - time;
return total;
} catch (ConnectionException e) {
throwException(_session, e.getMessage() );
return -1;
}finally{
if ( connection != null ){
try {
connection.logout();
} catch (ConnectionException e) {}
}
}
}
private cfSQLQueryData convertToQuery(String query, List<Map<String,Object>> rows ) throws cfmRunTimeException{
Map<String,Integer> activeColumns = new HashMap<String,Integer>();
cfSQLQueryData queryResultData = new cfSQLQueryData( "SALESFORCE" );
queryResultData.setQueryString(query);
Iterator<Map<String,Object>> itRows = rows.iterator();
while ( itRows.hasNext() ) {
Map<String,Object> row = itRows.next();
queryResultData.addRow(1);
queryResultData.setCurrentRow( queryResultData.getSize() );
Iterator<String> it = row.keySet().iterator();
while ( it.hasNext() ){
String field = it.next();
if ( !activeColumns.containsKey( field ) ){
int newcolumn = queryResultData.addColumnData( field, cfArrayData.createArray(1), null );
activeColumns.put( field, newcolumn );
}
int column = activeColumns.get( field );
queryResultData.setCell( column, tagUtils.convertToCfData( row.get(field) ) );
it.remove();
}
itRows.remove();
}
// Reset the internal counters
queryResultData.reset();
return queryResultData;
}
/**
* Runs the actual SalesForce Query
*
* @param connection
* @param soql
* @return
* @throws ConnectionException
* @throws cfmRunTimeException
*/
private int runQuery(cfSession _session, PartnerConnection connection, String soql, cfComponentData cfcCallback ) throws ConnectionException, cfmRunTimeException{
int totalRecords = 0;
QueryResult queryResults = connection.query(soql);
List<Map<String,Object>> rows = new ArrayList<Map<String,Object>>();
if (queryResults.getSize() > 0) {
boolean bWasRowAdded = false;
int recordsRxd = 0;
totalRecords = queryResults.getSize();
while ( recordsRxd < totalRecords ){
for (SObject s: queryResults.getRecords()) {
recordsRxd++;
Map<String,Object> rowMap = new HashMap<String,Object>();
bWasRowAdded = false;
Iterator<XmlObject> childrenIT = s.getChildren();
while (childrenIT.hasNext()) {
XmlObject xmlobject = childrenIT.next();
if ( !xmlobject.getChildren().hasNext() ){
String k = xmlobject.getName().getLocalPart();
Object v = s.getField(k);
if ( v != null ){
if ( k.equals("type") )
k = "objectType";
rowMap.put( k, v );
}
}else{
String namespace = xmlobject.getName().getLocalPart() + "__";
Iterator<XmlObject> subchildrenIT = xmlobject.getChildren();
while ( subchildrenIT.hasNext() ){
XmlObject cxmlobject = subchildrenIT.next();
if ( cxmlobject.getName().getLocalPart().equals("records") ){
Map<String,Object> innerMap = new HashMap<String,Object>();
innerMap.putAll(rowMap);
Iterator<XmlObject> recordsIT = cxmlobject.getChildren();
while ( recordsIT.hasNext() ){
XmlObject rxmlobject = recordsIT.next();
Object v = rxmlobject.getValue();
if ( v != null ){
String k = rxmlobject.getName().getLocalPart();
if ( k.equals("type") )
k = "objectType";
innerMap.put( namespace + k, v );
}
}
rows.add(innerMap);
innerMap.clear();
bWasRowAdded = true;
}else{
Object v = cxmlobject.getValue();
if ( v != null ){
String k = cxmlobject.getName().getLocalPart();
if ( k.equals("type") )
k = "objectType";
rowMap.put( namespace + k, v );
}
}
}
}
}
// Add the row to our list
if ( !bWasRowAdded ){
rows.add(rowMap);
}
}
// Now we can make the call out to the CFC
callback( _session, convertToQuery(soql, rows), recordsRxd, totalRecords, cfcCallback );
rows.clear();
if ( queryResults.isDone() )
break;
else
queryResults = connection.queryMore(queryResults.getQueryLocator());
}
}
return totalRecords;
}
/**
* Perform the call back
*
* @param convertToQuery
* @param cfcCallback
*/
private void callback(cfSession _session, cfSQLQueryData sqlQryData, int recordsin, int totalrecords, cfComponentData cfcCallback) throws cfmRunTimeException {
try {
ObjectCFC objectcfc = PluginManager.getPlugInManager().createCFC(_session, cfcCallback);
objectcfc.addArgument("qry", sqlQryData );
objectcfc.addArgument("recordsin", new cfNumberData(recordsin) );
objectcfc.addArgument("recordstotal", new cfNumberData(totalrecords) );
if ( !objectcfc.runMethodReturnBoolean(_session, "onSalesForceQueryResult" ) )
throwException(_session, "CFC returned false; cancelling the callback");
} catch (Exception e) {
throwException( _session, e.getMessage() );
}
}
}