* Separate the new callout extension interface from the classic callout interface. this is to avoid confusion since the methodName argument is not used here and implementation is expected to implement the start method.

* Change the getCallout method to findCallout and return a list of registered callout for a column. Callout is not a singleton service and developer can register multiple callout per column
* Make consistent with current semantic where callout is not invoke if record is processed and not always updateable.
* Make consistent with current semantic where only non empty string return from the callout instance is return to the caller as an error message string.
This commit is contained in:
Heng Sin Low 2010-03-09 00:27:33 +00:00
parent f73caa3e98
commit 052a829023
5 changed files with 177 additions and 100 deletions

View File

@ -58,7 +58,7 @@ Instead, you specify Table and Column in the extension declaration.
</documentation>
<appinfo>
<meta.attribute kind="java" basedOn=":org.compiere.model.Callout"/>
<meta.attribute kind="java" basedOn=":org.adempiere.base.IColumnCallout"/>
</appinfo>
</annotation>
</attribute>

View File

@ -9,14 +9,14 @@ import org.compiere.model.Callout;
/**
* This is a facade class for the Service Locator.
* It provides simple access to all core services.
*
*
* @author viola
*/
public class Core {
public static IResourceFinder getResourceFinder() {
return new IResourceFinder() {
public URL getResource(String name) {
List<IResourceFinder> f = Service.list(IResourceFinder.class);
for (IResourceFinder finder : f) {
@ -29,13 +29,13 @@ public class Core {
};
}
public static Callout getCallout(String tableName, String columnName) {
public static List<IColumnCallout> findCallout(String tableName, String columnName) {
ServiceQuery query = new ServiceQuery();
query.put("tableName", tableName);
query.put("columnName", columnName);
return Service.locate(Callout.class, query);
return Service.list(IColumnCallout.class, query);
}
}

View File

@ -0,0 +1,48 @@
/******************************************************************************
* 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 *
*****************************************************************************/
package org.adempiere.base;
import java.util.Properties;
import org.compiere.model.GridField;
import org.compiere.model.GridTab;
/**
* Column Callout Interface
*/
public interface IColumnCallout extends IService
{
/**
* Start Callout.
* <p>
* Callout's are used for cross field validation and setting values in other fields
* when returning a non empty (error message) string, an exception is raised
* <p>
* When invoked, the Tab model has the new value!
*
* @param ctx Context
* @param WindowNo current Window No
* @param mTab Model Tab
* @param mField Model Field
* @param value The new value
* @param oldValue The old value
* @return Error message or ""
*/
public String start (Properties ctx, int WindowNo,
GridTab mTab, GridField mField, Object value, Object oldValue);
}

View File

@ -27,7 +27,7 @@ import org.adempiere.base.IService;
* @author Jorg Janke
* @version $Id: Callout.java,v 1.2 2006/07/30 00:51:05 jjanke Exp $
*/
public interface Callout extends IService
public interface Callout
{
/**
* Start Callout.

View File

@ -38,6 +38,7 @@ import javax.script.ScriptEngine;
import javax.swing.event.EventListenerList;
import org.adempiere.base.Core;
import org.adempiere.base.IColumnCallout;
import org.adempiere.base.Service;
import org.compiere.util.CLogMgt;
import org.compiere.util.CLogger;
@ -72,7 +73,7 @@ import org.compiere.util.ValueNamePair;
* </pre>
* @author Jorg Janke
* @version $Id: GridTab.java,v 1.10 2006/10/02 05:18:39 jjanke Exp $
*
*
* @author Teo Sarca, SC ARHIPAC SERVICE SRL
* <li>BF [ 1742159 ] Editable number field for inactive record
* <li>BF [ 1968598 ] Callout is not called if tab is processed
@ -86,7 +87,7 @@ import org.compiere.util.ValueNamePair;
* https://sourceforge.net/tracker/?func=detail&aid=2874109&group_id=176962&atid=879332
* <li>BF [ 2905287 ] GridTab query is not build correctly
* https://sourceforge.net/tracker/?func=detail&aid=2905287&group_id=176962&atid=879332
* @author Victor Perez , e-Evolution.SC
* @author Victor Perez , e-Evolution.SC
* <li>FR [1877902] Implement JSR 223 Scripting APIs to Callout
* <li>BF [ 2910358 ] Error in context when a field is found in different tabs.
* https://sourceforge.net/tracker/?func=detail&aid=2910358&group_id=176962&atid=879332
@ -102,12 +103,12 @@ import org.compiere.util.ValueNamePair;
public class GridTab implements DataStatusListener, Evaluatee, Serializable
{
/**
*
*
*/
private static final long serialVersionUID = -8762357519103152929L;
public static final String DEFAULT_STATUS_MESSAGE = "NavigateOrUpdate";
/**
* Create Tab (Model) from Value Object.
* <p>
@ -120,7 +121,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
{
this(vo, w, false);
}
/**
* Create Tab (Model) from Value Object.
* <p>
@ -196,13 +197,13 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
/** Logger */
protected CLogger log = CLogger.getCLogger(getClass());
private boolean m_parentNeedSave = false;
private long m_lastDataStatusEventTime;
private DataStatusEvent m_lastDataStatusEvent;
// Context property names:
public static final String CTX_KeyColumnName = "_TabInfo_KeyColumnName";
public static final String CTX_LinkColumnName = "_TabInfo_LinkColumnName";
@ -213,7 +214,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
public static final String CTX_AD_Table_ID = "_TabInfo_AD_Table_ID";
public static final String CTX_FindSQL = "_TabInfo_FindSQL";
public static final String CTX_SQL = "_TabInfo_SQL";
/**************************************************************************
@ -258,7 +259,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
{
return m_loadComplete;
}
/**
* Initialize Tab with record from AD_Tab_v
* @param async async
@ -268,7 +269,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
{
log.fine("#" + m_vo.TabNo + " - Async=" + async + " - Where=" + m_vo.WhereClause);
if (isLoadComplete()) return true;
if (m_loader != null && m_loader.isAlive())
{
waitLoadCompete();
@ -289,9 +290,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
} // initTab
protected boolean loadTab()
{
{
m_extendedWhere = m_vo.WhereClause;
// Get Field Data
if (!loadFields())
{
@ -305,7 +306,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
m_loadComplete = true;
return true;
}
/**
* Dispose - clean up resources
*/
@ -412,7 +413,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
}
} // for all fields
if (! m_mTable.getTableName().equals(X_AD_PInstance_Log.Table_Name)) { // globalqss, bug 1662433
if (! m_mTable.getTableName().equals(X_AD_PInstance_Log.Table_Name)) { // globalqss, bug 1662433
// Add Standard Fields
if (m_mTable.getField("Created") == null)
{
@ -609,7 +610,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
public void query (boolean onlyCurrentRows, int onlyCurrentDays, int maxRows)
{
if (!m_loadComplete) initTab(false);
log.fine("#" + m_vo.TabNo
+ " - Only Current Rows=" + onlyCurrentRows
+ ", Days=" + onlyCurrentDays + ", Detail=" + isDetail());
@ -645,7 +646,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
{
String value = null;
if ( m_parentColumnName.length() > 0 )
{
{
// explicit parent link defined
value = Env.getContext(m_vo.ctx, m_vo.WindowNo, getParentTabNo(), m_parentColumnName, true);
if (value == null || value.length() == 0)
@ -654,8 +655,8 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
value = Env.getContext(m_vo.ctx, m_vo.WindowNo, getParentTabNo(), lc, true);
if (value == null || value.length() == 0)
value = Env.getContext(m_vo.ctx, m_vo.WindowNo, lc, true); // back compatibility
}
}
// Same link value?
if (refresh)
refresh = m_linkValue.equals(value);
@ -933,25 +934,25 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
{
if (hasChangedCurrentTabAndParents())
return false;
boolean retValue = (m_mTable.dataSave(manualCmd) == GridTable.SAVE_OK);
if (manualCmd)
{
setCurrentRow(m_currentRow, false);
if (m_lastDataStatusEvent != null && m_lastDataStatusEvent.getCurrentRow() == m_currentRow
&& ((m_lastDataStatusEvent.Record_ID != null && m_lastDataStatusEvent.Record_ID instanceof Integer
if (m_lastDataStatusEvent != null && m_lastDataStatusEvent.getCurrentRow() == m_currentRow
&& ((m_lastDataStatusEvent.Record_ID != null && m_lastDataStatusEvent.Record_ID instanceof Integer
&& (Integer) m_lastDataStatusEvent.Record_ID == 0) || m_lastDataStatusEvent.Record_ID == null))
{
updateDataStatusEventProperties(m_lastDataStatusEvent);
}
}
fireStateChangeEvent(new StateChangeEvent(this, StateChangeEvent.DATA_SAVE));
if (retValue) {
// refresh parent tabs
refreshParents();
}
return retValue;
}
catch (Exception e)
@ -1052,7 +1053,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
log.fine("#" + m_vo.TabNo);
m_mTable.dataIgnore();
setCurrentRow(m_currentRow, false); // re-load data
fireStateChangeEvent(new StateChangeEvent(this, StateChangeEvent.DATA_IGNORE));
log.fine("#" + m_vo.TabNo + "- fini");
} // dataIgnore
@ -1085,11 +1086,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
}
log.finest("Processed=" + processed);
}
//hengsin, dont create new when parent is empty
if (isDetail() && m_parentNeedSave)
return false;
/**
* temporary set currentrow to point to the new row to ensure even cause by m_mTable.dataNew
* is handle properly.
@ -1101,7 +1102,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
if (!retValue)
return retValue;
setCurrentRow(m_currentRow + 1, true);
// process all Callouts (no dependency check - assumed that settings are valid)
for (int i = 0; i < getFieldCount(); i++)
processCallout(getField(i));
@ -1112,7 +1113,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
getField(i).validateValue();
}
m_mTable.setChanged(false);
fireStateChangeEvent(new StateChangeEvent(this, StateChangeEvent.DATA_NEW));
return retValue;
} // dataNew
@ -1193,7 +1194,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
{
return m_keyColumnName;
} // getKeyColumnName
/**
* Set Name of the Key Column
* @param keyColumnName
@ -1226,9 +1227,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
m_parentColumnName = DB.getSQLValueString(null, sql, m_vo.Parent_Column_ID );
if ( m_parentColumnName == null )
m_parentColumnName = "";
if (linkColumnName != null)
m_linkColumnName = linkColumnName;
else
@ -1344,7 +1345,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
*/
public boolean isDetail()
{
// First Tab Level is not a detail
// First Tab Level is not a detail
if (m_vo.TabLevel == 0)
return false;
// We have IsParent columns and/or a link column
@ -1406,7 +1407,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
{
if (m_vo.IsReadOnly)
return true;
//hengsin, make detail readonly when parent is empty
if (m_parentNeedSave) return true;
@ -1790,7 +1791,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
if (filled)
return mf.format (arguments);
return " ";
@ -1857,7 +1858,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
if (filled)
return mf.format (arguments);
return " ";
@ -1935,7 +1936,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
if (colFax != null && (colFax.getVFormat() == null || colFax.getVFormat().length() == 0))
fFax.setVFormat(phone_frm);
}
} // loadDependentInfo
@ -2202,11 +2203,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
}
else // Redistribute Info with current row info
fireDataStatusChanged(m_DataStatusEvent);
//reset
m_lastDataStatusEventTime = System.currentTimeMillis();
m_lastDataStatusEvent = m_DataStatusEvent;
m_DataStatusEvent = null;
m_DataStatusEvent = null;
// log.fine("dataStatusChanged #" + m_vo.TabNo + "- fini", e.toString());
} // dataStatusChanged
@ -2325,9 +2326,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
// Row range check
int newRow = verifyRow(targetRow);
// Check, if we have old uncommitted data
if (m_mTable.dataSave(newRow, false) == false)
if (m_mTable.dataSave(newRow, false) == false)
return m_currentRow;
//remove/ignore new and unchange row
@ -2337,7 +2338,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
newRow--;
dataIgnore();
}
// new position
return setCurrentRow(newRow, true);
} // navigate
@ -2427,7 +2428,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
// Object value = null;
// if (mField.isKey() || mField.isParent() || mField.getColumnName().equals(m_linkColumnName))
// value = mField.getDefault();
// CarlosRuiz - globalqss [ 1881480 ] Navigation problem between tabs
// the implementation of linking with window context variables is very weak
// you must be careful when defining a column in a detail tab with a field
@ -2453,7 +2454,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
{
m_DataStatusEvent = m_lastDataStatusEvent;
}
// inform APanel/.. -> dataStatus with row updated
if (m_DataStatusEvent == null)
m_DataStatusEvent = new DataStatusEvent(this, getRowCount(),
@ -2465,10 +2466,10 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
if (status == null || status.length() == 0)
m_DataStatusEvent.setInfo(DEFAULT_STATUS_MESSAGE, null, false,false);
fireDataStatusChanged(m_DataStatusEvent);
//reset
m_DataStatusEvent = null;
return m_currentRow;
} // setCurrentRow
@ -2565,10 +2566,10 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
return "NoField";
log.fine(field.getColumnName() + "=" + value + " - Row=" + m_currentRow);
if (DisplayType.isID(field.getDisplayType()) && value instanceof Integer && ((Integer)value).intValue() < 0)
value = null;
int col = m_mTable.findColumn(field.getColumnName());
m_mTable.setValueAt(value, m_currentRow, col, false);
//
@ -2643,12 +2644,12 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
} // for all dependent fields
} // processDependencies
private List<String> activeCallouts = new ArrayList<String>();
private List<Callout> activeCalloutInstance = new ArrayList<Callout>();
/**
*
*
* @return list of active call out for this tab
*/
public String[] getActiveCallouts()
@ -2656,9 +2657,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
String[] list = new String[activeCallouts.size()];
return activeCallouts.toArray(list);
}
/**
*
*
* @return list of active call out instance for this tab
*/
public Callout[] getActiveCalloutInstance()
@ -2684,20 +2685,48 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
*/
public String processCallout (GridField field)
{
Object value = field.getValue();
Object oldValue = field.getOldValue();
Callout co = Core.getCallout(getTableName(), field.getColumnName());
if (co!=null) {
return co.start(m_vo.ctx, "", m_vo.WindowNo, this, field, value, oldValue);
}
String callout = field.getCallout();
if (callout.length() == 0)
return "";
//
if (isProcessed() && !field.isAlwaysUpdateable()) // only active records
return ""; // "DocProcessed";
Object value = field.getValue();
Object oldValue = field.getOldValue();
List<IColumnCallout> callouts = Core.findCallout(getTableName(), field.getColumnName());
if (callouts != null && !callouts.isEmpty()) {
for(IColumnCallout co : callouts)
{
String retValue = "";
String cmd = co.getClass().getName();
//detect infinite loop
if (activeCallouts.contains(cmd)) continue;
try
{
activeCallouts.add(cmd);
retValue = co.start(m_vo.ctx, m_vo.WindowNo, this, field, value, oldValue);
}
catch (Exception e)
{
log.log(Level.SEVERE, "start", e);
retValue = "Callout Invalid: " + e.toString();
return retValue;
}
finally
{
activeCallouts.remove(cmd);
}
if (!Util.isEmpty(retValue)) // interrupt on first error
{
log.severe (retValue);
return retValue;
}
}
}
String callout = field.getCallout();
if (callout.length() == 0)
return "";
log.fine(field.getColumnName() + "=" + value
+ " (" + callout + ") - old=" + oldValue);
@ -2705,26 +2734,26 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
while (st.hasMoreTokens()) // for each callout
{
String cmd = st.nextToken().trim();
//detect infinite loop
if (activeCallouts.contains(cmd)) continue;
String retValue = "";
// FR [1877902]
// CarlosRuiz - globalqss - implement beanshell callout
// Victor Perez - vpj-cd implement JSR 223 Scripting
if (cmd.toLowerCase().startsWith(MRule.SCRIPT_PREFIX)) {
MRule rule = MRule.get(m_vo.ctx, cmd.substring(MRule.SCRIPT_PREFIX.length()));
if (rule == null) {
retValue = "Callout " + cmd + " not found";
retValue = "Callout " + cmd + " not found";
log.log(Level.SEVERE, retValue);
return retValue;
}
if ( ! (rule.getEventType().equals(MRule.EVENTTYPE_Callout)
if ( ! (rule.getEventType().equals(MRule.EVENTTYPE_Callout)
&& rule.getRuleType().equals(MRule.RULETYPE_JSR223ScriptingAPIs))) {
retValue = "Callout " + cmd
+ " must be of type JSR 223 and event Callout";
+ " must be of type JSR 223 and event Callout";
log.log(Level.SEVERE, retValue);
return retValue;
}
@ -2734,7 +2763,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
// Window context are W_
// Login context are G_
MRule.setContext(engine, m_vo.ctx, m_vo.WindowNo);
// now add the callout parameters windowNo, tab, field, value, oldValue to the engine
// now add the callout parameters windowNo, tab, field, value, oldValue to the engine
// Method arguments context are A_
engine.put(MRule.ARGUMENTS_PREFIX + "WindowNo", m_vo.WindowNo);
engine.put(MRule.ARGUMENTS_PREFIX + "Tab", this);
@ -2743,7 +2772,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
engine.put(MRule.ARGUMENTS_PREFIX + "OldValue", oldValue);
engine.put(MRule.ARGUMENTS_PREFIX + "Ctx", m_vo.ctx);
try
try
{
activeCallouts.add(cmd);
retValue = engine.eval(rule.getScript()).toString();
@ -2758,7 +2787,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
{
activeCallouts.remove(cmd);
}
} else {
Callout call = null;
@ -2799,9 +2828,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
activeCallouts.remove(cmd);
activeCalloutInstance.remove(call);
}
}
}
if (!Util.isEmpty(retValue)) // interrupt on first error
{
log.severe (retValue);
@ -2870,7 +2899,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
return null;
return m_mTable.getValueAt(row, col);
} // getValue
/*
public boolean isNeedToSaveParent()
{
@ -2924,11 +2953,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
{
m_listenerList.add(DataStatusListener.class, l);
}
/**
* @param l
*/
public synchronized void addStateChangeListener(StateChangeListener l)
public synchronized void addStateChangeListener(StateChangeListener l)
{
m_listenerList.add(StateChangeListener.class, l);
}
@ -2936,11 +2965,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
/**
* @param l
*/
public synchronized void removeStateChangeListener(StateChangeListener l)
public synchronized void removeStateChangeListener(StateChangeListener l)
{
m_listenerList.remove(StateChangeListener.class, l);
}
/**
* Feature Request [1707462]
* Enable runtime change of VFormat
@ -2951,7 +2980,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
public void setFieldVFormat (String identifier, String strNewFormat)
{
m_mTable.setFieldVFormat(identifier, strNewFormat);
} // setFieldVFormat
} // setFieldVFormat
/**
* Switches the line/seqNo of the two rows
@ -3010,10 +3039,10 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
if (lineNoCurrentRow >= 9900
|| lineNoNextRow >= 9900) {
log.fine("don't sort - might be special lines");
return;
return;
}
// switch the line numbers and save new values
m_mTable.setValueAt(lineNoCurrentRow, to, lineCol);
setCurrentRow(to, false);
m_mTable.dataSave(true);
@ -3028,7 +3057,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
}
navigate(to);
}
private void fireStateChangeEvent(StateChangeEvent e)
{
StateChangeListener[] listeners = m_listenerList.getListeners(StateChangeListener.class);
@ -3037,11 +3066,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
for(int i = 0; i < listeners.length; i++) {
listeners[i].stateChange(e);
}
}
/**
*
*
* @return list of all tabs included in this tab
*/
public List<GridTab> getIncludedTabs()
@ -3064,8 +3093,8 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
}
return list;
}
//BF [ 2910358 ]
//BF [ 2910358 ]
/**
* get Parent Tab No
* @return Tab No
@ -3079,9 +3108,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
return tabNo;
while (parentLevel != currentLevel)
{
tabNo--;
tabNo--;
currentLevel = Env.getContextAsInt(m_vo.ctx, m_vo.WindowNo, tabNo, GridTab.CTX_TabLevel);
}
return tabNo;
}
}
} // GridTab