com.novartis.opensource.yada.QueryManager Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2016 Novartis Institutes for BioMedical Research Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.novartis.opensource.yada;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.servlet.http.Cookie;
import javax.xml.soap.SOAPConnection;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.novartis.opensource.yada.adaptor.FileSystemAdaptor;
import com.novartis.opensource.yada.adaptor.JDBCAdaptor;
import com.novartis.opensource.yada.adaptor.RESTAdaptor;
import com.novartis.opensource.yada.adaptor.SOAPAdaptor;
import com.novartis.opensource.yada.adaptor.YADAAdaptorException;
import com.novartis.opensource.yada.util.QueryUtils;
import com.novartis.opensource.yada.util.YADAUtils;
/**
* QueryManager is a workhorse class that performs essential query preparation
* tasks prior to execution. These tasks include retrieving {@link YADAQuery}
* objects using {@link Finder#getQuery(String)}, and creating global and
* query-based data structures for storing and mapping source connections,
* statements, and results.
*
* @since 4.0.0
* @author David Varon
* @see Service#execute()
* @see Service#handleRequest(javax.servlet.http.HttpServletRequest)
*/
public class QueryManager {
/**
* Local logger handle
*/
private static Logger l = Logger.getLogger(QueryManager.class);
/**
* Local handle to configuration object
*/
private YADARequest yadaReq;
/**
* Local handle to configuration object
*/
private JSONParams jsonParams;
/**
* Index of queries passed in request
*/
private YADAQuery[] queries;
/**
* Utility object
*/
private QueryUtils qutils = new QueryUtils();
/**
* Map of sources (JNDI strings) to JDBC or SOAP connections
*/
private HashMap connectionMap = new HashMap<>();
/**
* Map of SOAP query names to url strings
*/
private HashMap soapMap = new HashMap<>();
/**
* Map of REST query names to url strings
*/
private HashMap urlMap = new HashMap<>();
/**
* List of sources for which to defer commit execution, usu due to unsupported
* {@link ResultSet#HOLD_CURSORS_OVER_COMMIT}
*/
private List deferredCommits = new ArrayList<>();
/**
* Set of sources for which to commit execution is required—effectively
* any JDBC source in a {@link YADAQuery} with an {@code INSERT},
* {@code UPDATE}, or {@code DELETE} statement
*/
private Set requiredCommits = new HashSet<>();
/**
* Constant equal to: {@value}
*/
@SuppressWarnings("unused")
private final static String UNDEFINED = "undefined";
/**
* Constant equal to: {@value}
*/
@SuppressWarnings("unused")
private final static String NULLSTRING = "null";
/**
* Constructor stores config object and JSONParams if necessary, then calls
* effectively bootstraps query management process by calling
* {@link #processQueries()}
*
* @since 4.0.0
* @param yadaReq YADA request configuration
* @throws YADAUnsupportedAdaptorException when the adaptor can't be
* instantiated, or when it can't be
* found
* @throws YADAFinderException when there is an issue retrieving a
* query from the YADA index
* @throws YADAConnectionException when there is an issue opening a
* connection to a source referenced by
* a query
* @throws YADAResourceException when a query's source attribute can't
* be found in the application context,
* or there is another problem with the
* context
* @throws YADAQueryConfigurationException if request does not contain either a
* {@code qname} or {@code q}, or a
* {@code JSONParams} or {@code j}
* parameter
* @throws YADAAdaptorException when a query cannot be built by the
* adaptor
* @throws YADARequestException when filters are included in the
* request config, but can't be
* converted into a JSONObject
* @throws YADAParserException when a query code cannot be parsed
* successfully
*/
public QueryManager(YADARequest yadaReq)
throws YADAQueryConfigurationException, YADAResourceException, YADAConnectionException, YADAFinderException,
YADAUnsupportedAdaptorException, YADARequestException, YADAAdaptorException, YADAParserException {
processRequest(yadaReq);
}
/**
* Default constructor
*
* @since 7.1.0
*/
public QueryManager() {
}
/**
* A broker method which calls {@link #endowQuery(String)} (for standard param
* requests) or {@link #endowQueries(JSONParams)} for json params requests,
* followed by setting harmony maps, and {@link #prepQueriesForExecution()} in
* succession
*
* @param yadaReq the {@link YADARequest} to process
* @throws YADAFinderException when there is an issue retrieving a
* query from the YADA index
* @throws YADAConnectionException when there is an issue opening a
* connection to a source referenced by
* a query
* @throws YADAQueryConfigurationException if request does not contain either a
* {@code qname} or {@code q}, or a
* {@code JSONParams} or {@code j}
* parameter
* @throws YADAUnsupportedAdaptorException when the adaptor can't be
* instantiated, or when it can't be
* found
* @throws YADAResourceException when a query's source attribute can't
* be found in the application context,
* or there is another problem with the
* context
* @throws YADAAdaptorException when a query cannot be built by the
* adaptor
* @throws YADARequestException when filters are included in the
* request config, but can't be
* converted into a JSONObject
* @throws YADAParserException when query code cannot be parsed
* successfully
* @since 7.1.0
*/
private void processRequest(YADARequest yadaReq)
throws YADAQueryConfigurationException, YADAConnectionException, YADAFinderException, YADAResourceException,
YADAUnsupportedAdaptorException, YADARequestException, YADAAdaptorException, YADAParserException {
setYADAReq(yadaReq);
if (YADAUtils.hasJSONParams(getYADAReq()))
{
setJsonParams(yadaReq.getJsonParams());
setQueries(endowQueries(getJsonParams()));
}
else if (YADAUtils.hasQname(getYADAReq()))
{
setQueries(new YADAQuery[] { endowQuery(getYADAReq().getQname()) });
}
else
{
String msg = "Your request must contain a 'qname', 'q', 'JSONParams', or 'j' parameter.";
throw new YADARequestException(msg);
}
setGlobalHarmonyMaps();
setQueryHarmonyMaps();
prepQueriesForExecution();
}
/**
* A broker method which calls {@link #endowQuery(String)} (for standard param
* requests) or {@link #endowQueries(JSONParams)} for json params requests,
* followed by {@link #prepQueriesForExecution()} in succession
*
* @throws YADAFinderException when there is an issue retrieving a
* query from the YADA index
* @throws YADAConnectionException when there is an issue opening a
* connection to a source referenced by
* a query
* @throws YADAQueryConfigurationException if request does not contain either a
* {@code qname} or {@code q}, or a
* {@code JSONParams} or {@code j}
* parameter
* @throws YADAUnsupportedAdaptorException when the adaptor can't be
* instantiated, or when it can't be
* found
* @throws YADAResourceException when a query's source attribute can't
* be found in the application context,
* or there is another problem with the
* context
* @throws YADAAdaptorException when a query cannot be built by the
* adaptor
* @throws YADARequestException when filters are included in the
* request config, but can't be
* converted into a JSONObject
* @throws YADAParserException when query code cannot be parsed
* successfully
* @since 4.0.0
* @deprecated as of 7.1.0
*/
@SuppressWarnings("unused")
@Deprecated
private void processQueries()
throws YADAQueryConfigurationException, YADAConnectionException, YADAFinderException, YADAResourceException,
YADAUnsupportedAdaptorException, YADARequestException, YADAAdaptorException, YADAParserException {
if (YADAUtils.hasJSONParams(this.yadaReq)) // TODO this is a redundant call, see the constructor
{
setQueries(endowQueries(getJsonParams()));
}
else if (YADAUtils.hasQname(this.yadaReq))
{
setQueries(new YADAQuery[] { endowQuery(this.yadaReq.getQname()) });
}
else
{
String msg = "Your request must contain a 'qname', 'q', 'JSONParams', or 'j' parameter.";
throw new YADARequestException(msg);
}
setGlobalHarmonyMaps();
setQueryHarmonyMaps();
prepQueriesForExecution();
}
/**
* Create a composite harmony map for all queries based on either
* {@link YADARequest#getHarmonyMap()} or the harmony maps stored in each query
* via param {@link YADAQuery#getParam(String)} named
* {@link YADARequest#PS_HARMONYMAP}. This object, consistent in each request,
* enables inclusion of both mapped and unmapped columns in a single result,
* specifically necessary for {@link YADARequest#FORMAT_CSV} and other delimited
* formats.
*
* @since 6.1.0
*/
private void setGlobalHarmonyMaps() {
JSONArray reqHm = this.yadaReq.getHarmonyMap();
JSONObject globalHarmonyMap = new JSONObject();
if (null == reqHm)
{
for (YADAQuery yq: getQueries())
{
// YADAParam p = yq.getParam(YADARequest.PS_HARMONYMAP).get(0);
// if(p != null)
// {
if (yq.hasParam(YADARequest.PS_HARMONYMAP) && yq.getParam(YADARequest.PS_HARMONYMAP).size() > 0)
{
YADAParam p = yq.getParam(YADARequest.PS_HARMONYMAP).get(0);
JSONObject j = new JSONObject(p.getValue());
if (j.length() > 0)
{
globalHarmonyMap = populateGlobalHarmonyMap(globalHarmonyMap, j);
}
}
}
for (YADAQuery yq: getQueries())
yq.setGlobalHarmonyMap(globalHarmonyMap);
}
else
{
for (int i = 0; i < reqHm.length(); i++)
{
JSONObject j = reqHm.getJSONObject(i);
if (j.length() > 0)
{
globalHarmonyMap = populateGlobalHarmonyMap(globalHarmonyMap, j);
}
}
for (YADAQuery yq: getQueries())
yq.setGlobalHarmonyMap(globalHarmonyMap);
}
}
/**
* Populates a {@link JSONObject} with the unique set of keys, i.e., original
* column or field names passed into the request in {@link JSONParams} or in the
* {@link YADARequest#PS_HARMONYMAP} parameter.
*
* @param global The {@link JSONObject} to populate
* @param local The {@link JSONObject} containing the key/value pairs to
* transfer to {@code global}
* @return the {@code global} {@link JSONObject} containing the unique set of
* keys (and values)
* @since 6.1.0
*/
private JSONObject populateGlobalHarmonyMap(JSONObject global, JSONObject local) {
for (String key: JSONObject.getNames(local))
{
try
{
global.putOnce(key, local.get(key));
}
catch (JSONException e)
{
String msg = "Key [" + key + "] already exists in global harmony map.";
l.warn(msg);
}
}
return global;
}
/**
* Checks for a {@code harmonyMap} or {@code h} spec in the {@link #yadaReq}. If
* {@code null}, iterates over the queries in the request, identifying those
* that have embedded {@code h} specs and those that do not. If any queries
* contain specs, those that do not are provided with empty maps.
*
* @throws YADARequestException when the resulting harmonyMap is non-compliant
* @since 6.1.0
*/
private void setQueryHarmonyMaps() throws YADARequestException {
if (this.yadaReq.getHarmonyMap() == null)
{
ArrayList hasMap = new ArrayList<>();
ArrayList noMap = new ArrayList<>();
for (YADAQuery yq: this.queries)
{
if (yq.hasParam(YADARequest.PS_HARMONYMAP) && yq.getParam(YADARequest.PS_HARMONYMAP).size() > 0)
{
YADAParam p = yq.getParam(YADARequest.PS_HARMONYMAP).get(0);
hasMap.add(new JSONObject(p.getValue()));
}
else
{
noMap.add(yq);
}
}
if (hasMap.size() > 0)
{
for (YADAQuery yq: noMap)
{
YADAParam param = new YADAParam(YADARequest.PS_HARMONYMAP, "{}", YADAParam.QUERY, YADAParam.OVERRIDEABLE);
yq.addParam(param);
}
}
}
}
/**
* Adds the JDBC or SOAP connection object for {@code yq} to the internal index.
* If the connection is not yet in the index, it is created from the source
* stored in the {@code yq} object. If the protocol value in the query is not
* SOAP or JDBC, the method exits silently
*
* @param yq the query object containing the source string for setting the
* connection
* @throws YADAConnectionException when the connection to the source in the
* query cannot be set
*/
private void storeConnection(YADAQuery yq) throws YADAConnectionException {
String app = yq.getApp();
String protocol = yq.getProtocol();
try
{
if (!this.connectionMap.containsKey(app)
|| (protocol.equals(Parser.JDBC) && ((Connection) this.connectionMap.get(app)).isClosed()))
{
yq.setConnection();
this.connectionMap.put(app, yq.getConnection());
}
else
{
if (protocol.equals(Parser.JDBC))
yq.setConnection((Connection) this.connectionMap.get(app));
else if (protocol.equals(Parser.SOAP))
yq.setSOAPConnection((SOAPConnection) this.connectionMap.get(app));
}
}
catch (SQLException e)
{
String msg = "Unable to close connection";
throw new YADAConnectionException(msg, e);
}
}
/**
* Executes a query-level commit on the connection stored in the YADAQuery
* referenced by the parameter. If the connection object returned by the query
* is not a JDBC connection, but, for instance, a SOAPConnection, the error will
* be caught and handled gracefully.
*
* @param yq the query containing the statements to commit
* @throws YADAConnectionException when the commit fails
*/
public void commit(YADAQuery yq) throws YADAConnectionException {
try
{
if (this.requiredCommits.contains(yq.getApp()))
{
Connection connection = (Connection) yq.getConnection();
if (connection.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT)
{
connection.commit();
int count = yq.getResult().getTotalResultCount();
String rows = count == 1 ? "row" : "rows";
String msg = "\n------------------------------------------------------------\n";
msg += " Commit successful on connection to [" + yq.getApp() + "] (" + count + " " + rows + ")\n";
msg += "------------------------------------------------------------\n";
l.debug(msg);
}
else if(!connection.getAutoCommit())
{
deferCommit(yq.getApp());
}
}
}
catch (SQLException e)
{
String msg = "Unable to commit transaction on [" + yq.getApp() + "].";
throw new YADAConnectionException(msg, e);
}
catch (ClassCastException e)
{
l.info("Connection to [" + yq.getApp()
+ "] is not a JDBC connection (it's probably SOAP.) No commit was attempted.");
}
}
/**
* Executes a commit on all connections created during processing of the current
* request.
*
* @throws YADAConnectionException when the commit fails
*/
public void commit() throws YADAConnectionException {
if (this.connectionMap != null && this.connectionMap.keySet().size() > 0)
{
// TODO int totalCount = 0;
String source = "";
for (Iterator iterator = this.requiredCommits.iterator(); iterator.hasNext();)
{
try
{
source = iterator.next();
Connection connection = (Connection) this.connectionMap.get(source);
if (connection.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT)
{
connection.commit();
String msg = "\n------------------------------------------------------------\n";
msg += " Commit successful on [" + source + "].\n";
msg += "------------------------------------------------------------\n";
l.info(msg);
}
else if(!connection.getAutoCommit())
{
deferCommit(source);
}
}
catch (SQLException e)
{
String msg = "Unable to commit transaction on [" + source + "].";
throw new YADAConnectionException(msg, e);
}
catch (ClassCastException e)
{
l.info("Connection to [" + source
+ "] is not a JDBC connection (it's probably SOAP.) No commit was attempted.");
}
}
}
}
/**
* Adds {@code source} to the internal {@code #deferredCommits} list for
* execution of commit on the connection after results are parsed.
*
* @param app the name of the YADA app mapped to the datasource config
* @since 4.2.0
*/
private void deferCommit(String app) {
this.deferredCommits.add(app);
String msg = "Commit deferred on [" + app
+ "]. This is done, most likely, because the JDBC driver for this source does not support holdability.";
l.info(msg);
}
/**
* This method attempts (and usually suceeds) to close all JDBC resources opened
* during processing of the current request, including {@link ResultSet}s
* {@link java.sql.PreparedStatement}s and {@link java.sql.CallableStatement}s
* using the utility methods in
* {@link com.novartis.opensource.yada.ConnectionFactory}
*
* @throws YADAConnectionException when there is a problem closing any of the
* resources
* @see ConnectionFactory#releaseResources(ResultSet)
* @see ConnectionFactory#releaseResources(java.sql.Statement)
*/
public void releaseResources() throws YADAConnectionException {
for (String app: this.deferredCommits)
{
try
{
Connection connection = (Connection) this.connectionMap.get(app);
if(connection.getAutoCommit())
{
String msg = "Auto-commited";
l.info(msg);
}
else
{
connection.commit();
String msg = "\n------------------------------------------------------------\n";
msg += " Commit successful on [" + app + "].\n";
msg += "------------------------------------------------------------\n";
l.info(msg);
}
}
catch (SQLException e)
{
String msg = "\n------------------------------------------------------------\n";
msg += " Unable to commit transaction on [" + app + "].\n";
msg += "------------------------------------------------------------\n";
l.error(msg); // TODO should there be a rollback message here?
}
}
for (YADAQuery yq: this.getQueries())
{
YADAQueryResult yqr = yq.getResult();
if (yqr != null && yqr.getResults() != null)
{
for (Object result: yqr.getResults())
{
if (result instanceof ResultSet)
{
l.debug("Closing ResultSet");
ConnectionFactory.releaseResources((ResultSet) result);
}
}
}
if (yq.getPstmt() != null && yq.getPstmt().size() > 0)
{
for (PreparedStatement p: yq.getPstmt())
{
l.debug("Closing PreparedStatement");
ConnectionFactory.releaseResources(p);
l.debug("PreparedStatement removed from map.");
}
}
if (yq.getPstmtForCount() != null && yq.getPstmtForCount().values().size() > 0)
{
for (PreparedStatement p: yq.getPstmtForCount().values())
{
l.debug("Closing PreparedStatement for count query");
ConnectionFactory.releaseResources(p);
}
}
if (yq.getCstmt() != null && yq.getCstmt().size() > 0)
{
for (CallableStatement c: yq.getCstmt())
{
l.debug("Closing CallableStatement");
ConnectionFactory.releaseResources(c);
}
}
if (yq.getConnection() != null)
{
l.debug("Closing Connection");
ConnectionFactory.releaseResources((Connection) yq.getConnection());
}
}
this.connectionMap.clear();
l.debug("QueryManager connection map has been cleared.");
}
/**
* Adds the SQL function statement to the internal index
*
* @param yq the query object
* @param code the raw code
*/
private void storeCallableStatement(YADAQuery yq, String code) {
yq.addCstmt(this.qutils.getCallableStatement(code, (Connection) yq.getConnection()));
}
/**
* Adds the JDBC statement to the internal index
*
* @param yq the query containing the {@code code} and the index to which to
* add the statement
* @param code the SQL to map to the statement
* @throws YADAConnectionException when the statement is not yet in the map, and
* the connection deliver it
*/
@SuppressWarnings("unused")
private void storePreparedStatement(YADAQuery yq, String code) throws YADAConnectionException {
PreparedStatement p = this.qutils.getPreparedStatement(code, (Connection) yq.getConnection());
yq.addPstmt(p);
}
/**
* Adds the JDBC statement to the internal index at the specified position
*
* @param row the index of {@link YADAQuery}{@code .pstmt} at which to store
* the {@link PreparedStatement}
* @param yq the query containing the {@code code} and the index to which to
* add the statement
* @param code the SQL to map to the statement
* @throws YADAConnectionException when the statement is not yet in the map, and
* the connection deliver it
* @since 7.0.0
*/
private void storePreparedStatement(YADAQuery yq, String code, int row) throws YADAConnectionException {
PreparedStatement p = this.qutils.getPreparedStatement(code, (Connection) yq.getConnection());
yq.addPstmt(p, row);
}
/**
* Adds the JDBC statement to the internal index
*
* @param yq the query containing the {@code code} and the index to which to
* add the statement
* @param p the statement for the data query, serving as a key for the count
* statement in the map
* @param code the SQL to map to the statement
* @throws YADAConnectionException when the statement is not yet in the map, and
* the connection deliver it
*/
private void storePreparedStatementForCount(YADAQuery yq, PreparedStatement p, String code)
throws YADAConnectionException {
// TODO this won't work with CountOnly
PreparedStatement pc = this.qutils.getPreparedStatement(code, (Connection) yq.getConnection());
yq.addPstmtForCount(p, pc);
}
/**
* Adds the SOAP message to the internal index
*
* @param yq the query containing the code and index
* @param code the soap message
*/
private void storeSoapMessage(YADAQuery yq, String code) {
if (this.soapMap.containsKey(code))
{
yq.addSoap(this.soapMap.get(code));
}
else
{
yq.addSoap(this.qutils.getSoap(code));
}
}
/**
* Adds the REST url string to the internal index
*
* @param yq the query containing the code and index
* @param code the rest url string
*/
private void storeRestQuery(YADAQuery yq, String code) {
if (this.urlMap.containsKey(code))
{
yq.addUrl(this.urlMap.get(code));
}
else
{
yq.addUrl(code);
}
}
/**
* Prepares the query for execution by retrieving the wrapped query code,
* amending it if needed, as prescribed by request parameters, and links the
* query statement with it's database connection.
*
* @param yq the {@link YADAQuery} to prep
*
* @since 7.0.0
* @throws YADAResourceException when a query's source attribute can't
* be found in the application context,
* or there is another problem with the
* context
* @throws YADAUnsupportedAdaptorException when there is no adaptor available
* for the source or protocol or the
* intended adaptor can't be
* instantiated
* @throws YADAConnectionException when a connection to the source can't
* be opened
* @throws YADARequestException when filters are included in the
* request config, but can't be
* converted into a JSONObject
* @throws YADAAdaptorException when the query cannot be built by the
* adaptor
* @throws YADAParserException when the query code cannot be parsed
* successfully
*/
void prepQueryForExecution(YADAQuery yq) throws YADAResourceException, YADAUnsupportedAdaptorException,
YADAConnectionException, YADARequestException, YADAAdaptorException, YADAParserException {
// yq.addCoreCode(0, yq.getYADACode());
String conformedCode = yq.getConformedCode();
String wrappedCode = "";
int dataSize = yq.getData().size() > 0 ? yq.getData().size() : 1;
if (this.qutils.requiresConnection(yq))
{
storeConnection(yq);
}
this.qutils.processStatement(yq);
if (yq.getProtocol().equals(Parser.JDBC))
{
if (yq.getType().equals(Parser.CALL))
{
for (int row = 0; row < dataSize; row++)
{
wrappedCode = ((JDBCAdaptor) yq.getAdaptor()).buildCall(conformedCode).toString();
String msg = "\n------------------------------------------------------------";
msg += "\n Callable statement to execute:";
msg += "\n------------------------------------------------------------\n";
msg += wrappedCode.toString() + "\n";
l.debug(msg);
storeCallableStatement(yq, wrappedCode);
}
this.requiredCommits.add(yq.getApp());
}
else
{
boolean count = Boolean.parseBoolean(yq.getYADAQueryParamValue(YADARequest.PS_COUNT)[0]);
boolean countOnly = Boolean.parseBoolean(yq.getYADAQueryParamValue(YADARequest.PS_COUNTONLY)[0]);
int pageStart = Integer.parseInt(yq.getYADAQueryParamValue(YADARequest.PS_PAGESTART)[0]);
int pageSize = Integer.parseInt(yq.getYADAQueryParamValue(YADARequest.PS_PAGESIZE)[0]);
if (pageSize == -1)
pageSize = YADAUtils.ONE_BILLION;
int firstRow = 1 + (pageStart * pageSize) - pageSize;
String sortOrder = yq.getYADAQueryParamValue(YADARequest.PS_SORTORDER)[0];
String sortKey = "";
JSONObject filters = null;
if (yq.hasParamValue(YADARequest.PS_SORTKEY))
{
sortKey = yq.getYADAQueryParamValue(YADARequest.PS_SORTKEY)[0];
}
try
{
if (yq.hasParamValue(YADARequest.PS_FILTERS))
{
filters = new JSONObject(yq.getYADAQueryParamValue(YADARequest.PS_FILTERS)[0]);
// TODO add filter json schema validation, but not here--in setter
}
}
catch (JSONException e)
{
String msg = "Error while getting filters from parameters.";
throw new YADARequestException(msg, e);
}
for (int row = 0; row < dataSize; row++)
{
// int coreCodeIndex = dataSize > 1 ? row : 0;
if (yq.getInList() != null && yq.getInList().size() > 0)
{
this.qutils.processInList(yq, row);
}
if (yq.getValuesList() != null)
{
this.qutils.processValuesList(yq, row);
}
conformedCode = qutils.getConformedCode(yq.getCoreCode(row));
if (yq.getType().equals(Parser.SELECT))
{
// conformedCode = qutils.getConformedCode(yq.getCoreCode(row));
wrappedCode = ((JDBCAdaptor) yq.getAdaptor())
.buildSelect(conformedCode, sortKey, sortOrder, firstRow, pageSize, filters).toString();
String msg = "\n------------------------------------------------------------";
msg += "\n SELECT statement to execute:";
msg += "\n------------------------------------------------------------\n";
msg += wrappedCode.toString() + "";
msg += "\n------------------------------------------------------------\n";
l.debug(msg);
}
else // INSERT, UPDATE, DELETE
{
// conformedCode = qutils.getConformedCode(yq.getCoreCode(row));
wrappedCode = conformedCode;
this.requiredCommits.add(yq.getApp());
String msg = "\n------------------------------------------------------------";
msg += "\n INSERT/UPDATE/DELETE statement to execute:";
msg += "\n------------------------------------------------------------\n";
msg += wrappedCode.toString() + "";
msg += "\n------------------------------------------------------------\n";
l.debug(msg);
}
storePreparedStatement(yq, wrappedCode, row);
if (yq.getType().equals(Parser.SELECT) && (count || countOnly))
{
// conformedCode = qutils.getConformedCode(yq.getCoreCode(row));
wrappedCode = ((JDBCAdaptor) yq.getAdaptor()).buildSelectCount(conformedCode, filters).toString();
String msg = "\n------------------------------------------------------------";
msg += "\n SELECT COUNT statement to execute:";
msg += "\n------------------------------------------------------------\n";
msg += wrappedCode.toString() + "";
msg += "\n------------------------------------------------------------\n";
l.debug(msg);
storePreparedStatementForCount(yq, yq.getPstmt(row), wrappedCode);
}
if (yq.getStatement() != null)
this.qutils.setPositionalParameterValues(yq, row);
else
this.qutils.setValsInPosition(yq, row);
} // end data loop
} // end if callable
} // end if JDBC
else if (yq.getProtocol().equals(Parser.SOAP) || yq.getProtocol().equals(Parser.REST)
|| yq.getProtocol().equals(Parser.FILE))
{
if (yq.getType().equals(Parser.SOAP))
{
// TODO this may have introduced a bug (10-SEP-16)
yq.setSource(yq.getSource().replace(SOAPAdaptor.PROTOCOL_SOAP, SOAPAdaptor.PROTOCOL_HTTP));
for (int row = 0; row < dataSize; row++)
{
wrappedCode = ((SOAPAdaptor) yq.getAdaptor()).build(yq);
storeSoapMessage(yq, wrappedCode);
this.qutils.setValsInPosition(yq, row);
}
}
else if (yq.getType().equals(Parser.REST))
{
for (int row = 0; row < dataSize; row++)
{
wrappedCode = ((RESTAdaptor) yq.getAdaptor()).build(yq);
storeRestQuery(yq, wrappedCode);
this.qutils.setValsInPosition(yq, row);
}
}
else // filesystem
{
for (int row = 0; row < dataSize; row++)
{
wrappedCode = ((FileSystemAdaptor) yq.getAdaptor()).build(yq);
storeRestQuery(yq, wrappedCode);
this.qutils.setValsInPosition(yq, row);
}
}
}
else
{
String msg = "The query you are attempting to execute requires ";
msg += "a protocol or class that is not supported. This ";
msg += "could be the result of a configuration issue.";
throw new YADAUnsupportedAdaptorException(msg);
} // end protocols
}
/**
* @since 4.0.0
* @throws YADAResourceException when a query's source attribute can't
* be found in the application context,
* or there is another problem with the
* context
* @throws YADAUnsupportedAdaptorException when there is no adaptor available
* for the source or protocol or the
* intended adaptor can't be
* instantiated
* @throws YADAConnectionException when a connection to the source can't
* be opened
* @throws YADARequestException when filters are included in the
* request config, but can't be
* converted into a JSONObject
* @throws YADAAdaptorException when the query cannot be built by the
* adaptor
* @throws YADAParserException when the query code cannot be parsed
* successfully
*/
private void prepQueriesForExecution() throws YADAResourceException, YADAUnsupportedAdaptorException,
YADAConnectionException, YADARequestException, YADAAdaptorException, YADAParserException {
for (YADAQuery yq: this.getQueries())
{
prepQueryForExecution(yq);
} // query loop
}
/**
* Populates the data and parameter storage in the query object, using values
* passed in request object
*
* @since 4.0.0
* @param jSONParams the request param containing the query information to
* process
* @return array of {@link YADAQuery} objects corresponding to JSONParams
* entries
* @throws YADAFinderException when a query in {@code jsonParams}
* can't be found in the YADA index
* @throws YADAConnectionException when a connection to the YADA index
* can't be established
* @throws YADAQueryConfigurationException when the YADA request is malformed
* @throws YADAUnsupportedAdaptorException when the adaptor attached to the
* query object can't be found or
* instantiated
* @throws YADAResourceException when the query {@code q} can't be
* found in the index
*
*/
YADAQuery[] endowQueries(JSONParams jSONParams) throws YADAConnectionException, YADAFinderException,
YADAQueryConfigurationException, YADAResourceException, YADAUnsupportedAdaptorException {
Iterator jpIter = jSONParams.keySet().iterator();
int index = 0;
YADAQuery[] yqs = new YADAQuery[jSONParams.size()];
while (jpIter.hasNext())
{
String qname = jpIter.next();
YADAQuery yq = null;
if(Finder.hasYADALib())
yq = new Finder().getQuery(qname);
else
yq = new Finder().getQuery(qname, this.getUpdateStats());
yqs[index++] = endowQuery(yq, jSONParams.get(qname));
}
return yqs;
}
/**
* Populates the data and parameter storage in the {@code yq} object, using
* values passed {@code entry}
*
* @param yq the {@link YADAQuery} to augment
* @param entry the {@link JSONParamsEntry} containing the values to store in
* the query
* @return the augmented version of {@code yq}
* @since 4.2.0
* @throws YADAQueryConfigurationException when the YADA request is malformed
* @throws YADAUnsupportedAdaptorException when the adaptor attached to the
* query object can't be found or
* instantiated
* @throws YADAResourceException when the query {@code q} can't be
* found in the index
* @throws YADAConnectionException when a connection pool or string
* cannot be established
*
*/
YADAQuery endowQuery(YADAQuery yq, JSONParamsEntry entry) throws YADAQueryConfigurationException,
YADAResourceException, YADAUnsupportedAdaptorException, YADAConnectionException {
yq.addAllData(entry.getData());
yq.addYADAQueryParams(entry.getParams());
yq.setParameterizedColumns(yq.getColumnNameArray());
return endowQuery(yq);
}
/**
* Populates the data and parameter storage in the query object, using values
* passed in request object. When the request has a qname and params, it is for
* a singular query.
*
* @param q the name of the query to process
* @return a {@link YADAQuery} object retrieved from the YADA index
* corresponding to {@code q}
*
* @throws YADAFinderException when the query {@code q} can't be
* found in the YADA index
* @throws YADAConnectionException when a connection to the YADA index
* can't be established
* @throws YADAQueryConfigurationException whenthe YADA request is malformed
* @throws YADAUnsupportedAdaptorException when the adaptor attached to the
* query object can't be found or
* instantiated
* @throws YADAResourceException when the query {@code q} can't be
* found in the index
* @since 4.0.0
*/
YADAQuery endowQuery(String q) throws YADAConnectionException, YADAFinderException, YADAQueryConfigurationException,
YADAResourceException, YADAUnsupportedAdaptorException {
YADAQuery yq = null;
if(Finder.hasYADALib())
yq = new Finder().getQuery(q);
else
yq = new Finder().getQuery(q, this.getUpdateStats());
LinkedHashMap data = new LinkedHashMap<>();
String[][] params = this.yadaReq.getParams();
if (params != null)
{
for (int i = 0; i < params.length; i++)
{
data.put(QueryUtils.YADA_COLUMN + String.valueOf(i + 1), params[i]);
}
yq.addData(data);
yq.setParameterizedColumns(yq.getColumnNameArray());
}
yq.addParam(YADARequest.PS_QNAME, yq.getQname());
endowQuery(yq);
return yq;
}
/**
* Populates the data and parameter storage in the query object, using values
* passed in request object
*
* @since 4.0.0
* @param yq the query object to be processed
* @return {@code yq}, now endowed with metadata
* @throws YADAQueryConfigurationException when the YADA request is malformed
* @throws YADAUnsupportedAdaptorException when the adaptor attached to the
* query object can't be found or
* instantiated
* @throws YADAResourceException when the query {@code q} can't be
* found in the index
* @throws YADAConnectionException when a connection pool or string
* cannot be established
*/
YADAQuery endowQuery(YADAQuery yq) throws YADAQueryConfigurationException, YADAResourceException,
YADAUnsupportedAdaptorException, YADAConnectionException {
int index = 0;
if (getJsonParams() != null)
{
String qpath = yq.getApp() + "/" + yq.getQname();
index = ArrayUtils.indexOf(getJsonParams().getKeys(), qpath);
}
if(index == -1)
{
index = ArrayUtils.indexOf(getJsonParams().getKeys(), yq.getQname());
}
// get request params that pertain to the query
// at this point all default source and query params have set in the queries
yq.addRequestParams(this.yadaReq.getRequestParamsForQueries(), index);
yq.setAdaptorClass(this.qutils.getAdaptorClass(yq.getApp()));
if (RESTAdaptor.class.equals(yq.getAdaptorClass()))
{
if (this.getYADAReq().hasCookies())
{
for (String cookieName: this.yadaReq.getCookies())
{
for (Cookie cookie: this.yadaReq.getRequest().getCookies())
{
if (cookie.getName().equals(cookieName))
{
yq.addCookie(cookieName, Base64.encodeBase64String(Base64.decodeBase64(cookie.getValue().getBytes())));
}
}
}
}
if (this.getYADAReq().hasHttpHeaders())
{
JSONObject httpHeaders = this.yadaReq.getHttpHeaders();
@SuppressWarnings("unchecked")
Iterator keys = httpHeaders.keys();
while (keys.hasNext())
{
String name = keys.next();
yq.addHttpHeader(name, httpHeaders.getString(name));
}
}
}
@SuppressWarnings("unchecked")
Map conf = (Map) ConnectionFactory.getConnectionFactory().getDsConf().get(yq.getApp());
Properties props = new Properties();
if(conf.containsKey(ConnectionFactory.YADA_CONF_PROPS))
{
for(String key : JSONObject.getNames((JSONObject)conf.get(ConnectionFactory.YADA_CONF_PROPS)))
{
props.put(key, ((JSONObject)conf.get(ConnectionFactory.YADA_CONF_PROPS)).getString(key));
}
String paramStr = props.getProperty("params");
if (paramStr != null)
{
JSONArray yqp = new JSONArray(paramStr);
for (int i = 0; i < yqp.length(); i++)
{
JSONObject jo = yqp.getJSONObject(i);
YADAParam yp = new YADAParam();
String paramName = jo.getString("name");
String paramVal = jo.getString("value");
int paramRule = jo.getInt("rule");
for(String frag : YADAUtils.PARAM_FRAGS)
{
if(paramName.contentEquals(YADARequest.getParamKeyVal("PL_"+frag))
|| paramName.contentEquals(YADARequest.getParamKeyVal("PS_"+frag)))
{
try
{
this.getYADAReq().invokeSetter(YADARequest.getParamKeyVal("PS_"+frag), paramVal);
break;
}
catch (YADARequestException e)
{
String msg = "Could not set request parameter from stored value";
throw new YADAQueryConfigurationException(msg, e);
}
}
}
yp.setName(paramName);
yp.setValue(paramVal);
yp.setRule(paramRule);
List ypList = yq.getYADAQueryParamsForKey(paramName);
if(ypList.size() > 0)
{
YADAParam existingYp = ypList.get(0);
if(existingYp == null)
yq.addParam(yp);
else if(existingYp.getRule() == YADAParam.OVERRIDEABLE)
yq.getParam(paramName).get(0).setValue(paramVal);
}
else
{
yq.addParam(yp);
}
}
}
}
// TODO handle missing params exceptions here, throw YADARequestException
// TODO review instances where YADAQueryConfigurationException is thrown
this.qutils.setProtocol(yq);
yq.setAdaptor(this.qutils.getAdaptor(yq.getAdaptorClass(), this.yadaReq));
yq.setConformedCode(this.qutils.getConformedCode(yq.getYADACode()));
if(yq.getData().size() == 0)
{
yq.addCoreCode(0, yq.getYADACode());
}
for (int row = 0; row < yq.getData().size(); row++)
{
// TODO perhaps move this functionality to the deparsing step?
yq.addCoreCode(row, yq.getYADACode());
yq.addDataTypes(row, this.qutils.getDataTypes(yq.getYADACode()));
int paramCount = yq.getDataTypes().get(0).length;
yq.addParamCount(row, paramCount);
}
return yq;
}
/**
* Accessor for array of {@link YADAQuery} objects.
*
* @since 4.0.0
* @return array of YADAQuery objects
*/
public YADAQuery[] getQueries() {
return this.queries;
}
/**
* Accessor for {@link YADAQuery} at index.
*
* @param index the position of the desired query in the internal array
* @return YADAQuery at the desired index
*/
public YADAQuery getQuery(int index) {
return this.getQueries()[index];
}
/**
* Returns the value of {@link YADARequest#PS_UPDATE_STATS}
*
* @return {@code true} by default, or {@code false} if set deliberately in the
* request
*/
private boolean getUpdateStats() {
return this.yadaReq.getUpdateStats();
}
/**
* Standard accessor.
*
* @return the local {@link YADARequest}
*/
private YADARequest getYADAReq() {
return this.yadaReq;
}
/**
* Standard mutator for variable
*
* @param yadaReq YADA request configuration
*/
private void setYADAReq(YADARequest yadaReq) {
this.yadaReq = yadaReq;
}
/**
* Standard accessor for variable
*
* @return the jsonParams object
*/
private JSONParams getJsonParams() {
return this.jsonParams;
}
/**
* Standard mutator for variable
*
* @param jsonParams the config object to set
*/
private void setJsonParams(JSONParams jsonParams) {
this.jsonParams = jsonParams;
}
/**
* @param queries the queries to set
*/
private void setQueries(YADAQuery[] queries) {
this.queries = queries;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy