Refactoring for [ adempiere-Feature Requests-1877902 ] Implement JSR 223: Scripting callout

Created MRule
This commit is contained in:
Carlos Ruiz 2008-01-25 22:58:32 +00:00
parent c338371707
commit 452069c60e
6 changed files with 341 additions and 157 deletions

View File

@ -1086,15 +1086,6 @@ public class GridField
return m_vo.Callout; return m_vo.Callout;
} }
//FR [1877902]
/**
* Get Script Code
* @return Script Code
*/
public String getScriptCode()
{
return m_vo.scriptCode;
}
/** /**
* Get AD_Process_ID * Get AD_Process_ID
* @return process * @return process

View File

@ -140,52 +140,8 @@ public class GridFieldVO implements Serializable
vo.Description = rs.getString (i); vo.Description = rs.getString (i);
else if (columnName.equalsIgnoreCase("Help")) else if (columnName.equalsIgnoreCase("Help"))
vo.Help = rs.getString (i); vo.Help = rs.getString (i);
else if (columnName.equalsIgnoreCase("Callout")) { else if (columnName.equalsIgnoreCase("Callout"))
vo.Callout = rs.getString (i); vo.Callout = rs.getString (i);
// CarlosRuiz - globalqss - FR [ 1877902 ] Implement beanshell callout
// Vitor Perez - vpj-cd - FR [ 1877902 ] Implement JSR 223 Scripting APIs to Callout
String callout = vo.Callout;
int script_end = 8;
int engine_end = 0;
if (callout != null && callout.toLowerCase().startsWith("@script:")) {
engine_end = callout.indexOf(":", script_end);
if (engine_end <= 0)
{
CLogger.get().log(Level.SEVERE, "ColumnName=" + columnName, " Call Invalid "+ callout +" error in syntax please use something like @script:groovy:mycallout");
continue;
}
String engine = callout.substring(script_end,engine_end);
if (engine== null)
{
CLogger.get().log(Level.SEVERE, "ColumnName=" + columnName, " Call Invalid "+ callout +" error in syntax please use something like @script:groovy:mycallout");
continue;
}
String calloutname = callout.substring(engine_end);
if (calloutname== null)
{
CLogger.get().log(Level.SEVERE, "ColumnName=" + columnName, " Call Invalid "+ callout +" error in syntax please use something like @script:groovy:mycallout");
continue;
}
String rule_value =engine + calloutname.trim();
// TODO: Write MRule and create accessor by Value, EventType and RuleType
int script_id = DB.getSQLValue(
null,
"SELECT AD_Rule_ID FROM AD_Rule WHERE TRIM(Value)=? AND EventType='"
+ X_AD_Rule.EVENTTYPE_Callout
+ "' AND RuleType='"
+ X_AD_Rule.RULETYPE_JSR223ScriptingAPIs
+ "' AND IsActive='Y'",
rule_value);
if (script_id > 0) {
X_AD_Rule rule = new X_AD_Rule(ctx, script_id, null);
vo.scriptCode = rule.getScript();
// TODO: pre-parse for better performance
// http://beanshell.org/manual/parser.html#Parsing_and_Performance
} else {
CLogger.get().log(Level.SEVERE, "ColumnName=" + columnName, " Rule not found:" + rule_value);
}
}
}
else if (columnName.equalsIgnoreCase("AD_Process_ID")) else if (columnName.equalsIgnoreCase("AD_Process_ID"))
vo.AD_Process_ID = rs.getInt (i); vo.AD_Process_ID = rs.getInt (i);
else if (columnName.equalsIgnoreCase("ReadOnlyLogic")) else if (columnName.equalsIgnoreCase("ReadOnlyLogic"))
@ -496,8 +452,6 @@ public class GridFieldVO implements Serializable
public boolean IsParent = false; public boolean IsParent = false;
/** Callout */ /** Callout */
public String Callout = ""; public String Callout = "";
/** Callout */
public String scriptCode = null;
/** Process */ /** Process */
public int AD_Process_ID = 0; public int AD_Process_ID = 0;
/** Description */ /** Description */

View File

@ -28,14 +28,9 @@ import java.util.logging.*;
import javax.swing.event.*; import javax.swing.event.*;
//import org.compiere.apps.ADialog;
import org.compiere.util.*; import org.compiere.util.*;
import bsh.EvalError;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.Invocable;
import javax.script.ScriptException; import javax.script.ScriptException;
/** /**
@ -2453,93 +2448,50 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
// FR [1877902] // FR [1877902]
// CarlosRuiz - globalqss - implement beanshell callout // CarlosRuiz - globalqss - implement beanshell callout
// Victor Perez - vpj-cd implement JSR 223 Scripting // Victor Perez - vpj-cd implement JSR 223 Scripting
if (cmd.toLowerCase().startsWith("@script:")) { if (cmd.toLowerCase().startsWith(MRule.SCRIPT_PREFIX)) {
if (field.getScriptCode() == null || field.getScriptCode().length() == 0) { MRule rule = MRule.get(m_vo.ctx, cmd.substring(MRule.SCRIPT_PREFIX.length()));
retValue = "Callout invalid error in syntax: " + cmd + " error in syntax please use something like @script:groovy:mycallout"; if (rule == null) {
retValue = "Callout " + cmd + " not found";
log.log(Level.SEVERE, retValue); log.log(Level.SEVERE, retValue);
return retValue; return retValue;
} }
if ( ! (rule.getEventType().equals(MRule.EVENTTYPE_Callout)
int engine_end = 0; && rule.getRuleType().equals(MRule.RULETYPE_JSR223ScriptingAPIs))) {
engine_end = callout.indexOf(":", 8); retValue = "Callout " + cmd
if (engine_end <= 0) + " must be of type JSR 223 and event Callout";
{ log.log(Level.SEVERE, retValue);
CLogger.get().log(Level.SEVERE, "Callout Invalid: " + cmd , " error in syntax please use something like @script:groovy:mycallout"); return retValue;
return retValue;
}
String engine_name = callout.substring(8,engine_end);
if (engine_name== null)
{
CLogger.get().log(Level.SEVERE, "Callout Invalid: " + cmd , " error in syntax please use something like @script:groovy:mycallout");
return retValue;
}
// Convert from ctx to hashmap
HashMap<String, Object> script_ctx = new HashMap<String,Object>();
// Convert properties to HashMap
Enumeration en = m_vo.ctx.keys();
while (en.hasMoreElements())
{
String key = en.nextElement().toString();
// filter
if (key == null || key.length() == 0
|| key.startsWith("P") // Preferences
|| (key.indexOf('|') != -1 && !key.startsWith(String.valueOf(m_vo.WindowNo))) // other Window Settings
|| (key.indexOf('|') != -1 && key.indexOf('|') != key.lastIndexOf('|')) //other tab
)
continue;
Object obj = m_vo.ctx.get(key);
if (key != null && key.length() > 0)
{
//log.fine( "Script.setEnvironment " + key, value);
if (value == null)
script_ctx.remove(key);
else
script_ctx.put(convertKey(key, m_vo.WindowNo), obj);
}
} }
ScriptEngine engine = rule.getScriptEngine();
// Window context are _ // Window context are _
// Login context are __ // Login context are __
MRule.setContext(engine, m_vo.ctx, m_vo.WindowNo);
// now add the callout parameters windowNo, tab, field, value, oldValue to the engine
// Parameter context are ___ // Parameter context are ___
// Now Add windowNo, tab, field, value, oldValue to the hashmap engine.put("___WindowNo", m_vo.WindowNo);
script_ctx.put("___WindowNo", m_vo.WindowNo); engine.put("___Tab", this);
script_ctx.put("___Tab", this); engine.put("___Field", field);
script_ctx.put("___Field", field); engine.put("___Value", value);
script_ctx.put("___Value", value); engine.put("___OldValue", oldValue);
script_ctx.put("___OldValue", oldValue);
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName(engine_name);
try try
{ {
Iterator it = script_ctx.keySet().iterator(); activeCallouts.add(cmd);
while (it.hasNext()) retValue = engine.eval(rule.getScript()).toString();
{
String key = (String)it.next();
Object script_value = script_ctx.get(key);
if (script_value instanceof Boolean)
engine.put(key, ((Boolean)script_value).booleanValue());
else if (script_value instanceof Integer)
engine.put(key,((Integer)script_value).intValue());
else if (script_value instanceof Double)
engine.put(key,((Integer)script_value).intValue());
else
engine.put(key,script_value);
}
activeCallouts.add(cmd);
retValue = engine.eval(field.getScriptCode()).toString();
activeCallouts.remove(cmd);
} }
catch (ScriptException e) catch (Exception e)
{ {
log.log(Level.SEVERE, "", e); log.log(Level.SEVERE, "", e);
retValue = "Callout Invalid: " + e.toString(); retValue = "Callout Invalid: " + e.toString();
return retValue; return retValue;
} }
finally
{
activeCallouts.remove(cmd);
}
} else { } else {
@ -2575,7 +2527,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
log.log(Level.SEVERE, "start", e); log.log(Level.SEVERE, "start", e);
retValue = "Callout Invalid: " + e.toString(); retValue = "Callout Invalid: " + e.toString();
return retValue; return retValue;
} }
finally finally
{ {
activeCallouts.remove(cmd); activeCallouts.remove(cmd);
@ -2593,31 +2545,6 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
return ""; return "";
} // processCallout } // processCallout
/**
* Convert Key
* # -> _
* @param key
* @param m_windowNo
* @return converted key
*/
private String convertKey (String key, int m_windowNo)
{
String k = m_windowNo + "|";
if (key.startsWith(k))
{
String retValue = "_" + key.substring(k.length());
retValue = Util.replace(retValue, "|", "_");
return retValue;
}
else
{
String retValue = Util.replace(key, "#", "__");
return retValue;
}
} // convertKey
/** /**
* Get Value of Field with columnName * Get Value of Field with columnName
* @param columnName column name * @param columnName column name

View File

@ -0,0 +1,297 @@
/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program 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 this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
* Contributor(s): Carlos Ruiz - globalqss *
*****************************************************************************/
package org.compiere.model;
import java.sql.*;
import java.util.*;
import java.util.logging.*;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import org.compiere.util.*;
/**
* Persistent Rule Model
* @author Carlos Ruiz
* @version $Id: MRule.java
*/
public class MRule extends X_AD_Rule
{
/**
* Get Rule from Cache
* @param ctx context
* @param AD_Rule_ID id
* @return MRule
*/
public static MRule get (Properties ctx, int AD_Rule_ID)
{
Integer key = new Integer (AD_Rule_ID);
MRule retValue = (MRule) s_cache.get (key);
if (retValue != null)
return retValue;
retValue = new MRule (ctx, AD_Rule_ID, null);
if (retValue.get_ID () != 0)
s_cache.put (key, retValue);
return retValue;
} // get
/**
* Get Rule from Cache
* @param ctx context
* @param ruleValue case sensitive rule Value
* @return Rule
*/
public static MRule get (Properties ctx, String ruleValue)
{
if (ruleValue == null)
return null;
Iterator it = s_cache.values().iterator();
while (it.hasNext())
{
MRule retValue = (MRule)it.next();
if (ruleValue.equals(retValue.getValue()))
return retValue;
}
//
MRule retValue = null;
String sql = "SELECT * FROM AD_Rule WHERE Value=? AND IsActive='Y'";
PreparedStatement pstmt = null;
try
{
pstmt = DB.prepareStatement (sql, null);
pstmt.setString(1, ruleValue);
ResultSet rs = pstmt.executeQuery ();
if (rs.next ())
retValue = new MRule (ctx, rs, null);
rs.close ();
pstmt.close ();
pstmt = null;
}
catch (Exception e)
{
s_log.log(Level.SEVERE, sql, e);
}
try
{
if (pstmt != null)
pstmt.close ();
pstmt = null;
}
catch (Exception e)
{
pstmt = null;
}
if (retValue != null)
{
Integer key = new Integer (retValue.getAD_Rule_ID());
s_cache.put (key, retValue);
}
return retValue;
} // get
/**
* Get Model Validation Login Rules
* @param ctx context
* @return Rule
*/
public static ArrayList<MRule> getModelValidatorLoginRules (Properties ctx)
{
ArrayList<MRule> rules = new ArrayList<MRule>();
MRule rule = null;
String sql = "SELECT * FROM AD_Rule WHERE EventType=? AND IsActive='Y'";
PreparedStatement pstmt = null;
try
{
pstmt = DB.prepareStatement (sql, null);
pstmt.setString(1, EVENTTYPE_ModelValidatorLoginEvent);
ResultSet rs = pstmt.executeQuery ();
while (rs.next ()) {
rule = new MRule (ctx, rs, null);
rules.add(rule);
}
rs.close ();
pstmt.close ();
pstmt = null;
}
catch (Exception e)
{
s_log.log(Level.SEVERE, sql, e);
}
try
{
if (pstmt != null)
pstmt.close ();
pstmt = null;
}
catch (Exception e)
{
pstmt = null;
}
if (rules != null && rules.size() > 0)
return rules;
else
return null;
} // getModelValidatorLoginRules
/** Cache */
private static CCache<Integer,MRule> s_cache = new CCache<Integer,MRule>("AD_Rule", 20);
/** Static Logger */
private static CLogger s_log = CLogger.getCLogger (MRule.class);
public static final String SCRIPT_PREFIX = "@script:";
/* Engine Manager */
private ScriptEngineManager factory = null;
/* The Engine */
ScriptEngine engine = null;
/**************************************************************************
* Standard Constructor
* @param ctx context
* @param AD_Rule_ID id
* @param trxName transaction
*/
public MRule (Properties ctx, int AD_Rule_ID, String trxName)
{
super (ctx, AD_Rule_ID, trxName);
} // MRule
/**
* Load Constructor
* @param ctx context
* @param rs result set
* @param trxName transaction
*/
public MRule (Properties ctx, ResultSet rs, String trxName)
{
super(ctx, rs, trxName);
} // MRule
/**
* Before Save
* @param newRecord new
* @return true
*/
protected boolean beforeSave (boolean newRecord)
{
// Validate format for scripts
// must be engine:name
// where engine can be groovy, jython or beanshell
if (getRuleType().equals(RULETYPE_JSR223ScriptingAPIs)) {
String engineName = getEngineName();
if (engineName == null ||
(! (engineName.equalsIgnoreCase("groovy")
|| engineName.equalsIgnoreCase("jython")
|| engineName.equalsIgnoreCase("beanshell")))) {
log.saveError("Error", Msg.getMsg(getCtx(), "WrongScriptValue"));
return false;
}
}
return true;
} // beforeSave
/**
* String Representation
* @return info
*/
public String toString()
{
StringBuffer sb = new StringBuffer ("MRule[");
sb.append (get_ID()).append ("-").append (getValue()).append ("]");
return sb.toString ();
} // toString
/**
* Script Engine for this rule
* @return ScriptEngine
*/
public ScriptEngine getScriptEngine() {
factory = new ScriptEngineManager();
String engineName = getEngineName();
if (engineName != null)
engine = factory.getEngineByName(getEngineName());
return engine;
}
public String getEngineName() {
int colonPosition = getValue().indexOf(":");
if (colonPosition < 0)
return null;
return getValue().substring(0, colonPosition);
}
/**************************************************************************
* Set Context ctx to the engine based on windowNo
* @param engine ScriptEngine
* @param ctx context
* @param windowNo window number
*/
public static void setContext(ScriptEngine engine, Properties ctx, int windowNo) {
Enumeration<Object> en = ctx.keys();
while (en.hasMoreElements())
{
String key = en.nextElement().toString();
// filter
if (key == null || key.length() == 0
|| key.startsWith("P") // Preferences
|| (key.indexOf('|') != -1 && !key.startsWith(String.valueOf(windowNo))) // other Window Settings
|| (key.indexOf('|') != -1 && key.indexOf('|') != key.lastIndexOf('|')) //other tab
)
continue;
Object value = ctx.get(key);
if (value != null) {
if (value instanceof Boolean)
engine.put(convertKey(key, windowNo), ((Boolean)value).booleanValue());
else if (value instanceof Integer)
engine.put(convertKey(key, windowNo), ((Integer)value).intValue());
else if (value instanceof Double)
engine.put(convertKey(key, windowNo), ((Double)value).doubleValue());
else
engine.put(convertKey(key, windowNo), value);
}
}
}
/**
* Convert Key
* # -> _
* @param key
* @param m_windowNo
* @return converted key
*/
private static String convertKey (String key, int m_windowNo)
{
String k = m_windowNo + "|";
if (key.startsWith(k))
{
String retValue = "_" + key.substring(k.length());
retValue = Util.replace(retValue, "|", "_");
return retValue;
}
else
{
String retValue = Util.replace(key, "#", "__");
return retValue;
}
} // convertKey
} // MRule

View File

@ -0,0 +1,7 @@
-- Jan 24, 2008 10:27:22 PM COT
-- 1877902 - Implement JSR 223: Scripting callout
INSERT INTO AD_Message (AD_Client_ID,AD_Message_ID,AD_Org_ID,Created,CreatedBy,EntityType,IsActive,MsgText,MsgTip,MsgType,Updated,UpdatedBy,Value) VALUES (0,53023,0,TO_DATE('2008-01-24 22:27:19','YYYY-MM-DD HH24:MI:SS'),100,'D','Y','Wrong Script Value - format for JSR 223 Script must be engine:scriptName where supported engines must be something like groovy, jython, beanshell',NULL,'E',TO_DATE('2008-01-24 22:27:19','YYYY-MM-DD HH24:MI:SS'),100,'WrongScriptValue')
;
INSERT INTO AD_Message_Trl (AD_Language,AD_Message_ID, MsgText,MsgTip, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Message_ID, t.MsgText,t.MsgTip, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Message t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Message_ID=53023 AND EXISTS (SELECT * FROM AD_Message_Trl tt WHERE tt.AD_Language!=l.AD_Language OR tt.AD_Message_ID!=t.AD_Message_ID)
;

View File

@ -0,0 +1,8 @@
-- Jan 24, 2008 10:27:22 PM COT
-- 1877902 - Implement JSR 223: Scripting callout
INSERT INTO AD_Message (AD_Client_ID,AD_Message_ID,AD_Org_ID,Created,CreatedBy,EntityType,IsActive,MsgText,MsgTip,MsgType,Updated,UpdatedBy,Value) VALUES (0,53023,0,TO_TIMESTAMP('2008-01-24 22:27:19','YYYY-MM-DD HH24:MI:SS'),100,'D','Y','Wrong Script Value - format for JSR 223 Script must be engine:scriptName where supported engines must be something like groovy, jython, beanshell',NULL,'E',TO_TIMESTAMP('2008-01-24 22:27:19','YYYY-MM-DD HH24:MI:SS'),100,'WrongScriptValue')
;
INSERT INTO AD_Message_Trl (AD_Language,AD_Message_ID, MsgText,MsgTip, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Message_ID, t.MsgText,t.MsgTip, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Message t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Message_ID=53023 AND EXISTS (SELECT * FROM AD_Message_Trl tt WHERE tt.AD_Language!=l.AD_Language OR tt.AD_Message_ID!=t.AD_Message_ID)
;