/* * Copyright (C) 2000 - 2008 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/ */ package org.alanwilliamson.amazon.simpledb; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.aw20.amazon.SimpleDB; import com.naryx.tagfusion.cfm.engine.catchDataFactory; import com.naryx.tagfusion.cfm.engine.cfCatchData; import com.naryx.tagfusion.cfm.engine.cfData; import com.naryx.tagfusion.cfm.engine.cfSession; import com.naryx.tagfusion.cfm.engine.cfStringData; import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; import com.naryx.tagfusion.cfm.sql.cfSQLQueryData; import com.naryx.tagfusion.cfm.sql.preparedData; public class cfSimpleDBResultData extends cfSQLQueryData implements java.io.Serializable { private static final long serialVersionUID = 1L; private SimpleDB sdb; private String tokenPassed; public cfSimpleDBResultData(SimpleDB sdb, String tokenPassed){ super( "AmazonSimpleDb" ); this.sdb = sdb; this.tokenPassed = tokenPassed; } /* * Override the getData() so we can give access to other parameters * that the amazon implementation returns * @see com.naryx.tagfusion.cfm.sql.cfSQLQueryData#getData(java.lang.String) */ public cfData getData( String _key ) { if ( _key.equalsIgnoreCase("token") ){ return new cfStringData( this.sdb.getLastToken() ); }else if ( _key.equalsIgnoreCase("boxusage") ){ return new cfStringData( this.sdb.getLastBoxUsage() ); }else if ( _key.equalsIgnoreCase("requestid") ){ return new cfStringData( this.sdb.getLastRequestId() ); }else return super.getData( _key ); } protected String getExtraInfo( boolean _isLong ){ return ""; } public void execute( cfSession _Session ) throws cfmRunTimeException { if ( getQueryType() == cfSQLQueryData.SQL_UPDATE ){ throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "UPDATE statement not supported", null, queryString) ); } else if ( getQueryType() == cfSQLQueryData.SQL_DELETE ){ executeDelete( _Session ); } else if ( getQueryType() == cfSQLQueryData.SQL_INSERT ){ executeInsert( _Session ); } else if ( getQueryType() == cfSQLQueryData.SQL_SELECT ){ executeSelect( _Session ); } else { throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "Statement not supported", null, queryString) ); } } private preparedData currentPreparedData = null; private int preparedIndex = 0, preparedDataIndex = 0; private String getNextParam() throws cfmRunTimeException { if ( preparedDataList == null || preparedDataList.size() == 0 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "No CFQUERYPARAM", null, queryString) ); if ( currentPreparedData == null ) currentPreparedData = preparedDataList.get( preparedIndex++ ); if ( preparedDataIndex == currentPreparedData.getSize() ){ preparedDataIndex = 0; if ( preparedIndex == preparedDataList.size() ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "CFQUERYPARAM out of sync", null, queryString) ); currentPreparedData = preparedDataList.get( preparedIndex++ ); } cfData thisData = currentPreparedData.getData( preparedDataIndex++ ); String data; //Pad out the data if ( thisData.getDataType() == cfData.CFNUMBERDATA && currentPreparedData.getPadding() > 0 ){ data = thisData.getString(); int padLength = currentPreparedData.getPadding() - data.length(); for ( int x=0; x < padLength; x++ ){ data = "0" + data; } }else data = thisData.getString(); //Final check to make sure the string isn't too big if ( data.length() > 1024 ) return data.substring( 0, 1024 ); else return data; } @SuppressWarnings({ "rawtypes", "unchecked" }) private void executeInsert( cfSession _Session ) throws cfmRunTimeException { /* * Pull out the DOMAIN name ---------------------------------------------- */ int c1 = queryString.toLowerCase().indexOf("insert into"); int c2 = queryString.indexOf("("); if ( c1 == -1 || c2 == -1 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error should be [insert into myDomainName () values ()]", null, queryString) ); String domain = queryString.substring( c1 + 11, c2 ).trim(); if ( domain.length() == 0 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error invalid domain", null, queryString) ); c1 = queryString.indexOf(")", c2 + 1); /* * Pull out the column names ---------------------------------------------- */ String columnNames[] = queryString.substring( c2 + 1, c1 ).trim().split( ",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))" ); //Clean up and validate the columns boolean bFound_ItemName = false; for ( int x=0; x < columnNames.length; x++ ){ columnNames[x] = columnNames[x].trim(); if ( columnNames[x].length() == 0 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error invalid column name", null, queryString) ); if ( columnNames[x].length() >= 2 && columnNames[x].charAt(0) == '"' && columnNames[x].charAt(columnNames[x].length()-1) == '"' ) columnNames[x] = columnNames[x].substring( 1, columnNames[x].length()-1 ); if ( columnNames[x].equals("ItemName") && bFound_ItemName ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "You cannot include the column name 'ItemName' more than once", null, queryString) ); if ( columnNames[x].equals("ItemName") ) bFound_ItemName = true; } if ( !bFound_ItemName ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "You must include the column name 'ItemName' in the column list", null, queryString) ); /* * Pull out the values names ---------------------------------------------- */ c2 = queryString.indexOf("(", c1 + 1); c1 = queryString.lastIndexOf(")" ); if ( c2 == -1 || c1 == -1 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error invalid values (..)", null, queryString) ); String values[] = queryString.substring( c2 + 1, c1 ).trim().split( ",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))" ); if ( values.length != columnNames.length ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error number of columns do not match values", null, queryString) ); for ( int x=0; x < values.length; x++ ){ values[x] = values[x].trim(); if ( values[x].length() == 0 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error invalid value", null, queryString) ); if ( values[x].length() >= 2 && values[x].charAt(0) == '"' && values[x].charAt(values[x].length()-1) == '"' ) values[x] = values[x].substring( 1, values[x].length()-1 ); } /* * Create the Amazon DB call ---------------------------------------------- */ HashMap attributes = new HashMap(); String ItemName = null; for ( int x=0; x < columnNames.length; x++ ){ if ( values[x].equals("?") ) values[x] = getNextParam(); if ( !columnNames[x].equals("ItemName") ){ attributes.put( columnNames[x], values[x] ); }else{ ItemName = values[x]; } } try { executeTime = System.currentTimeMillis(); sdb.putAttributes(domain, ItemName, attributes, attributes.keySet() ); } catch (Exception e) { throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "AmazonError: " + e.getMessage(), null, queryString) ); } finally { executeTime = System.currentTimeMillis() - executeTime; } } @SuppressWarnings({ "rawtypes", "unchecked" }) private void executeDelete( cfSession _Session ) throws cfmRunTimeException { /* * Pull out the domain ---------------------------------------------- */ int c1 = queryString.toLowerCase().indexOf("delete from"); int c2 = queryString.toLowerCase().indexOf("where"); if ( c1 == -1 || c2 == -1 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error should be [delete from myDomainName where ItemName='' {AND ItemAttribute=''}]", null, queryString) ); String domain = queryString.substring( c1 + 11, c2 ).trim(); if ( domain.length() == 0 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error invalid domain", null, queryString) ); /* * Pull out the ItemName ---------------------------------------------- */ c1 = queryString.indexOf("ItemName"); if ( c1 == -1 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error: 'ItemName' was not found in WHERE clause", null, queryString) ); c2 = queryString.indexOf("=", c1+8); if ( c2 == -1 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error: 'ItemName' value not found in WHERE clause", null, queryString) ); String strleft = queryString.substring( c2 + 1 ).trim(); String ItemName = null; if ( strleft.charAt(0) == '\'' ){ c1 = strleft.indexOf( "'", 1 ); if ( c1 == -1 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error: 'ItemName' value not found in WHERE clause", null, queryString) ); ItemName = strleft.substring( 1, c1 ); if ( c1 == strleft.length() ) strleft = ""; else strleft = strleft.substring( c1 + 1 ); } else if ( strleft.charAt(0) == '"' ){ c1 = strleft.indexOf( "\"", 1 ); if ( c1 == -1 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error: 'ItemName' value not found in WHERE clause", null, queryString) ); ItemName = strleft.substring( 1, c1 ); if ( c1 == strleft.length() ) strleft = ""; else strleft = strleft.substring( c1 + 1 ); } else if ( strleft.charAt(0) == '?' ) { ItemName = getNextParam(); strleft = strleft.substring( 1 ); } else if ( ItemName == null ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error: 'ItemName' value not found in WHERE clause, use quotes or CFQUERYPARAM", null, queryString) ); strleft = strleft.trim(); /* * Pull out the ItemAttribute ---------------------------------------------- */ String ItemAttribute = null; c1 = strleft.indexOf("ItemAttribute"); if ( c1 != -1 ){ strleft = strleft.substring( c1 + 13 ).trim(); c2 = strleft.indexOf("="); if ( c2 == -1 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error: 'ItemAttribute' value not found in WHERE clause", null, queryString) ); strleft = strleft.substring( c2 + 1 ).trim(); if ( strleft.charAt(0) == '\'' ){ c1 = strleft.indexOf( "'", 1 ); if ( c1 == -1 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error: 'ItemAttribute' value not found in WHERE clause", null, queryString) ); ItemAttribute = strleft.substring( 1, c1 ); } else if ( strleft.charAt(0) == '"' ){ c1 = strleft.indexOf( "\"", 1 ); if ( c1 == -1 ) throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "SQL Parse Error: 'ItemAttribute' value not found in WHERE clause", null, queryString) ); ItemAttribute = strleft.substring( 1, c1 ); } else if ( strleft.charAt(0) == '?' ) { ItemAttribute = getNextParam(); } } /* * Now Prepare the call for amazon ---------------------------------------------- */ try { executeTime = System.currentTimeMillis(); if ( ItemAttribute == null ){ sdb.deleteAttributes(domain, ItemName); }else{ HashMap attributes = new HashMap(); attributes.put( ItemAttribute, null ); sdb.deleteAttributes(domain, ItemName, attributes ); } } catch (Exception e) { throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "AmazonError: " + e.getMessage(), null, queryString) ); } finally { executeTime = System.currentTimeMillis() - executeTime; } } @SuppressWarnings({ "unchecked", "rawtypes" }) private void executeSelect( cfSession _Session ) throws cfmRunTimeException { /* * Run the full query against amazon */ try { executeTime = System.currentTimeMillis(); resultSet = true; List<HashMap> results = sdb.select( queryString, tokenPassed ); if ( results.size() > 0 ){ Set columns = getUnqiueColumns( results ); init( (String[])columns.toArray(new String[0]), null, "AmazonSimpleDB" ); for ( Iterator<HashMap> it = results.iterator(); it.hasNext(); ){ HashMap item = it.next(); addRow(1); setCurrentRow( getSize() ); for( Iterator<String> kit = item.keySet().iterator(); kit.hasNext(); ){ String key = kit.next(); String val = (String)((String[])item.get( key ))[0]; setCell( key, new cfStringData(val) ); } } }else{ init( new String[]{"itemname"}, null, "AmazonSimpleDB" ); } } catch (Exception e) { throw new cfmRunTimeException( catchDataFactory.extendedException( cfCatchData.TYPE_DATABASE, "errorCode.sqlError", "AmazonError: " + e.getMessage(), null, queryString) ); } finally { executeTime = System.currentTimeMillis() - executeTime; } } @SuppressWarnings({ "rawtypes", "unchecked" }) private Set getUnqiueColumns( List<HashMap> results ){ HashSet columns = new HashSet(); for ( Iterator<HashMap> it = results.iterator(); it.hasNext(); ){ HashMap item = it.next(); columns.addAll( item.keySet() ); } return columns; } }