/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2014 ForgeRock AS. All rights reserved.
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://forgerock.org/license/CDDLv1.0.html
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at http://forgerock.org/license/CDDLv1.0.html
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*/
package org.forgerock.openidm.repo.orientdb.impl.query;
import java.util.HashMap;
import java.util.Map;
import org.forgerock.json.resource.ActionRequest;
import org.forgerock.json.resource.BadRequestException;
import org.forgerock.openidm.repo.QueryConstants;
import org.forgerock.openidm.repo.orientdb.impl.OrientDBRepoService;
import org.forgerock.openidm.smartevent.EventEntry;
import org.forgerock.openidm.smartevent.Name;
import org.forgerock.openidm.smartevent.Publisher;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.exception.OQueryParsingException;
import com.orientechnologies.orient.core.sql.OCommandSQL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Configured and add-hoc command support on OrientDB
*
* Commands can contain tokens of the format ${token-name}
*
*/
public class Commands extends ConfiguredQueries<OCommandSQL, ActionRequest, Integer> {
final static Logger logger = LoggerFactory.getLogger(Commands.class);
private static final String COMMAND_ID = "commandId";
private static final String COMMAND_EXPRESSION = "commandExpression";
public Commands() {
super(new HashMap<String, QueryInfo<OCommandSQL>>());
}
/**
* Create an SQL command to execute the command query.
*
* @param queryString the query expression, including tokens to replace
* @return the prepared query object
*/
protected OCommandSQL createQueryObject(String queryString) {
return new OCommandSQL(queryString);
}
/**
* Execute a command, either a pre-configured command by using the command ID, or a command expression passed as
* part of the params.
*
* The keys for the input parameters as well as the return map entries are in QueryConstants.
*
* @param type the relative/local resource name, which needs to be converted to match the OrientDB document class name
* @param request the query request, including parameters which include the query id, or the query expression, as well as the
* token key/value pairs to replace in the query
* @param database a handle to a database connection instance for exclusive use by the query method whilst it is executing.
* @return The query result, which includes meta-data about the query, and the result set itself.
* @throws org.forgerock.json.resource.BadRequestException if the passed request parameters are invalid, e.g. missing query id or query expression or tokens.
*/
public Integer query(final String type, final ActionRequest request, final ODatabaseDocumentTx database)
throws BadRequestException {
final Map<String, String> params = new HashMap<String, String>(request.getAdditionalParameters());
params.put(QueryConstants.RESOURCE_NAME, OrientDBRepoService.typeToOrientClassName(type));
if (params.get(COMMAND_ID) == null && params.get(COMMAND_EXPRESSION) == null) {
throw new BadRequestException("Either " + COMMAND_ID + " or " + COMMAND_EXPRESSION
+ " to identify/define a command must be passed in the parameters. " + params);
}
final QueryInfo<OCommandSQL> queryInfo;
try {
queryInfo = findQueryInfo(type, params.get(COMMAND_ID), params.get(COMMAND_EXPRESSION));
} catch (IllegalArgumentException e) {
throw new BadRequestException("The passed command identifier " + params.get(COMMAND_ID)
+ " does not match any configured commands on the OrientDB repository service.");
}
Integer result = null;
logger.debug("Evaluate command {}", queryInfo.getQueryString());
Name eventName = getEventName(params.get(COMMAND_ID), params.get(COMMAND_EXPRESSION));
EventEntry measure = Publisher.start(eventName, queryInfo, null);
// Disabled prepared statements until pooled usage is clarified
boolean tryPrepared = false;
try {
if (tryPrepared /* queryInfo.isUsePrepared() */ ) {
result = doPreparedQuery(queryInfo, params, database);
} else {
result = doTokenSubsitutionQuery(queryInfo, params, database);
}
measure.setResult(result);
return result;
} catch (OQueryParsingException firstTryEx) {
if (tryPrepared /* queryInfo.isUsePrepared() */ ) {
// Prepared query is invalid, fall back onto add-hoc resolved query
try {
logger.debug("Prepared version not valid, trying manual substitution");
result = doTokenSubsitutionQuery(queryInfo, params, database);
measure.setResult(result);
// Disable use of the prepared statement as manually resolved works
logger.debug("Manual substitution valid, mark not to use prepared statement");
queryInfo.setUsePrepared(false);
} catch (OQueryParsingException secondTryEx) {
// TODO: consider differentiating between bad configuration and bad request
throw new BadRequestException("Failed to resolve and parse the command "
+ queryInfo.getQueryString() + " with params: " + params, secondTryEx);
}
} else {
// TODO: consider differentiating between bad configuration and bad request
throw new BadRequestException("Failed to resolve and parse the command "
+ queryInfo.getQueryString() + " with params: " + params, firstTryEx);
}
} catch (IllegalArgumentException ex) {
// TODO: consider differentiating between bad configuration and bad request
throw new BadRequestException("Command is invalid: "
+ queryInfo.getQueryString() + " " + ex.getMessage(), ex);
} catch (RuntimeException ex) {
logger.warn("Unexpected failure during DB command: {}", ex.getMessage());
throw ex;
} finally {
measure.end();
}
return result;
}
}