package com.baasbox.commands; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.commons.lang3.exception.ExceptionUtils; import com.baasbox.commands.exceptions.CommandException; import com.baasbox.commands.exceptions.CommandExecutionException; import com.baasbox.commands.exceptions.CommandParsingException; import com.baasbox.dao.GenericDao; import com.baasbox.db.DbHelper; import com.baasbox.exception.SwitchUserContextException; import com.baasbox.service.logging.BaasBoxLogger; import com.baasbox.service.scripting.base.JsonCallback; import com.baasbox.service.scripting.js.Json; import com.baasbox.util.JSONFormats; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.BooleanNode; import com.fasterxml.jackson.databind.node.NullNode; import com.google.common.collect.ImmutableMap; import com.orientechnologies.orient.core.exception.OQueryParsingException; import com.orientechnologies.orient.core.exception.OSecurityAccessException; import com.orientechnologies.orient.core.id.ORID; import com.orientechnologies.orient.core.record.impl.ODocument; /** * Created by eto on 22/09/14. */ class DBResource extends Resource { public static final Resource INSTANCE = new DBResource(); @Override public String name() { return "db"; } @Override public Map<String, ScriptCommand> commands() { return COMMANDS; } public static final Map<String,ScriptCommand> COMMANDS = ImmutableMap.<String,ScriptCommand>builder() .put("switchUser", DBResource::switchUser) .put("isAdmin",DBResource::isConnectedAsAdmin) .put("beginTransaction",DBResource::beginTransaction) .put("isInTransaction",DBResource::isInTransaction) .put("commitTransaction",DBResource::commitTransaction) .put("rollbackTransaction",DBResource::rollbackTransaction) .put("isAnId",DBResource::isAnId) .put("select",DBResource::select) .put("exec",DBResource::exec) .build(); // ImmutableMap.of("switchUser", DBResource::switchUser, // "transact", DBResource::runInTransaction, // "isAdmin",DBResource::isConnectedAsAdmin, // "isInTransaction",DBResource::isInTransaction, // "abortTransaction",DBResource::abortTransation); // private static final ThreadLocal<Boolean> JS_TRANSACTION_RUNNING = new ThreadLocal<Boolean>(){ // @Override // protected Boolean initialValue() { // return false; // } // }; private static JsonNode select(JsonNode c,JsonCallback callback) throws CommandException{ JsonNode jParams = c.get(ScriptCommand.PARAMS); String statement = jParams.get("query").asText(); JsonNode depthNode = jParams.get("depth"); String depth=""; if (depthNode!=null && depthNode.isTextual()) depth=",depth:"+depthNode.asText(); BaasBoxLogger.debug("Executing query from a plugin: " + statement); BaasBoxLogger.debug("...depth: " + depth); ArrayNode qryParams = (ArrayNode) jParams.get("array_of_params"); ArrayList params=new ArrayList(); if (qryParams!=null) qryParams.forEach(j->{ if (j==null) params.add(null); else params.add(j.asText()); }); ArrayNode lst; try { List listToReturn = (List) DbHelper.genericSQLStatementExecute("select " + statement, params.toArray()); String s = JSONFormats.prepareResponseToJson(listToReturn, JSONFormats.Formats.GENERIC+depth,true); BaasBoxLogger.debug("Query result: "); BaasBoxLogger.debug(s); lst = (ArrayNode)Json.mapper().readTree(s); } catch (IOException e) { throw new CommandExecutionException(c,"error executing command: "+ExceptionUtils.getMessage(e),e); } catch(OQueryParsingException e){ throw new CommandExecutionException(c,"Error parsing query: "+ExceptionUtils.getMessage(e),e); } return lst; } private static JsonNode exec(JsonNode c,JsonCallback callback) throws CommandException{ JsonNode jParams = c.get(ScriptCommand.PARAMS); String statement = jParams.get("statement").asText(); BaasBoxLogger.debug("Executing statement from a plugin: " + statement); ArrayNode qryParams = (ArrayNode) jParams.get("array_of_params"); ArrayList params=new ArrayList(); if (qryParams!=null) qryParams.forEach(j->{ if (j==null) params.add(null); else params.add(j.asText()); }); JsonNode lst; try { Object listToReturn = (Object) DbHelper.genericSQLStatementExecute(statement, params.toArray()); String s = ""; if (listToReturn instanceof List ) s=JSONFormats.prepareResponseToJson((List)listToReturn, JSONFormats.Formats.GENERIC,true); else if (listToReturn instanceof ODocument) s=JSONFormats.prepareResponseToJson((ODocument)listToReturn, JSONFormats.Formats.GENERIC,true); else if (listToReturn == null) s=null; else s=listToReturn.toString(); BaasBoxLogger.debug("Statement result: "); BaasBoxLogger.debug(s); if (s==null) lst=NullNode.getInstance(); else lst = Json.mapper().readTree(s); } catch (IOException e) { throw new CommandExecutionException(c,"error executing command: "+ExceptionUtils.getMessage(e),e); } catch(OQueryParsingException e){ throw new CommandExecutionException(c,"Error parsing statement: "+ExceptionUtils.getMessage(e),e); } return lst; } private static JsonNode beginTransaction(JsonNode c,JsonCallback callback) throws CommandException{ DbHelper.requestTransaction(); return NullNode.getInstance(); } private static JsonNode rollbackTransaction(JsonNode c,JsonCallback callback) throws CommandException{ DbHelper.rollbackTransaction(); return NullNode.getInstance(); } private static JsonNode commitTransaction(JsonNode command,JsonCallback callback) throws CommandException { DbHelper.commitTransaction(); return NullNode.getInstance(); } private static JsonNode switchUser(JsonNode command,JsonCallback callback) throws CommandException { try { DbHelper.reconnectAsAdmin(); return callback.call(NullNode.getInstance()); }catch (SwitchUserContextException e){ throw new CommandExecutionException(command,"Cannot switch to admin! Did you leave an open transaction?"); }finally { try{ DbHelper.reconnectAsAuthenticatedUser(); }catch(OSecurityAccessException e){ //if the script has changed username or password of the actual user, her credentials are not valid anymore and the db connection is lost BaasBoxLogger.warn("Database connection is not available inside a Plugin Script"); //swallow } } } private static JsonNode isConnectedAsAdmin(JsonNode command,JsonCallback callback) throws CommandException { return BooleanNode.valueOf(DbHelper.isConnectedAsAdmin(false)); } // private static JsonNode runInTransaction(JsonNode command,JsonCallback callback) throws CommandException{ // boolean commit = true; // try { // // DbHelper.requestTransaction(); // JS_TRANSACTION_RUNNING.set(true); // JsonNode res = callback.call(NullNode.getInstance()); // JS_TRANSACTION_RUNNING.set(false); // return res; // }catch (AbortTransaction t){ // commit = false; // return NullNode.getInstance(); // }catch (Exception e){ // commit = false; // throw new CommandExecutionException(command,ExceptionUtils.getMessage(e),e); // } finally { // if (commit){ // DbHelper.commitTransaction(); // } else { // DbHelper.rollbackTransaction(); // } // } // } private static JsonNode isInTransaction(JsonNode command,JsonCallback callback) throws CommandException { return BooleanNode.valueOf(DbHelper.isInTransaction()); } private static JsonNode isAnId(JsonNode command,JsonCallback callback) throws CommandException { validateHasParams(command); String id = null; try { id=getLinkId(command); }catch (CommandParsingException e){ return BooleanNode.valueOf(false); } ORID ret=GenericDao.getInstance().getRidNodeByUUID(id); return BooleanNode.valueOf(!(ret==null)); } private static void validateHasParams(JsonNode command) throws CommandParsingException{ if (!command.has(ScriptCommand.PARAMS)) { throw new CommandParsingException(command,"missing parameters"); } } private static String getLinkId(JsonNode command) throws CommandException{ JsonNode params = command.get(ScriptCommand.PARAMS); JsonNode id = params.get("id"); if (id==null||!id.isTextual()){ throw new CommandParsingException(command,"missing id"); } String idString = id.asText(); return idString; } }