/****************************************************************************** * This file is part of Adempiere ERP Bazaar * * http://www.adempiere.org * * * * Copyright (C) Heng Sin Low * * Copyright (C) Contributors * * * * 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. * * * * Contributors: * * - Teo Sarca * *****************************************************************************/ package org.adempiere.util; import java.math.BigDecimal; import java.sql.CallableStatement; import java.sql.ResultSet; import java.util.Properties; import java.util.logging.Level; import javax.script.ScriptEngine; import org.adempiere.base.Core; import org.adempiere.exceptions.AdempiereException; import org.compiere.model.MProcess; import org.compiere.model.MRule; import org.compiere.process.ProcessCall; import org.compiere.process.ProcessInfo; import org.compiere.process.ProcessInfoParameter; import org.compiere.process.ProcessInfoUtil; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Msg; import org.compiere.util.Trx; import org.compiere.wf.MWFProcess; import org.compiere.wf.MWorkflow; /** * Helper methods for server process * @author Low Heng Sin * @author Teo Sarca, SC ARHIPAC SERVICE SRL *
  • BF [ 1757523 ] Server Processes are using Server's context *
  • BF [ 2528297 ] Poor error message on jasper fail *
  • BF [ 2530847 ] Report is displayed even if java process fails */ public final class ProcessUtil { public static final String JASPER_STARTER_CLASS = "org.adempiere.report.jasper.ReportStarter"; /** Logger */ private static final CLogger log = CLogger.getCLogger(ProcessUtil.class); private ProcessUtil() {} /** * Start database store procedure * @param processInfo * @param ProcedureName * @param trx * @return boolean */ public static boolean startDatabaseProcedure (ProcessInfo processInfo, String ProcedureName, Trx trx) { return startDatabaseProcedure(processInfo, ProcedureName, trx, true); } /** * Start database store procedure * @param processInfo * @param ProcedureName * @param trx * @param managedTrx false if trx is managed by caller * @return boolean */ public static boolean startDatabaseProcedure (ProcessInfo processInfo, String ProcedureName, Trx trx, boolean managedTrx) { String sql = "{call " + ProcedureName + "(?)}"; String trxName = trx != null ? trx.getTrxName() : null; CallableStatement cstmt = null; try { //hengsin, add trx support, updateable support. cstmt = DB.prepareCall(sql, ResultSet.CONCUR_UPDATABLE, trxName); cstmt.setInt(1, processInfo.getAD_PInstance_ID()); cstmt.executeUpdate(); if (trx != null && trx.isActive() && managedTrx) { trx.commit(true); } } catch (Exception e) { log.log(Level.SEVERE, sql, e); if (trx != null && trx.isActive() && managedTrx) { trx.rollback(); } processInfo.setSummary (Msg.getMsg(Env.getCtx(), "ProcessRunError") + " " + e.getLocalizedMessage()); processInfo.setError (true); return false; } finally { DB.close(cstmt); cstmt = null; if (trx != null && managedTrx) trx.close(); } return true; } @Deprecated public static boolean startJavaProcess(ProcessInfo pi, Trx trx) { return startJavaProcess(Env.getCtx(), pi, trx); } /** * @param ctx * @param pi * @param trx * @return true if process completed successfully */ public static boolean startJavaProcess(Properties ctx, ProcessInfo pi, Trx trx) { return startJavaProcess(ctx, pi, trx, true); } /** * @param ctx * @param pi * @param trx * @param managedTrx false if trx is managed by caller * @return true if process completed successfully */ public static boolean startJavaProcess(Properties ctx, ProcessInfo pi, Trx trx, boolean managedTrx) { return startJavaProcess(ctx, pi, trx, managedTrx, null); } /** * @param ctx * @param pi * @param trx * @param managedTrx false if trx is managed by caller * @return true if process completed successfully */ public static boolean startJavaProcess(Properties ctx, ProcessInfo pi, Trx trx, boolean managedTrx, IProcessUI processMonitor) { String className = pi.getClassName(); if (className == null) { MProcess proc = new MProcess(ctx, pi.getAD_Process_ID(), trx.getTrxName()); if (proc.getJasperReport() != null) className = JASPER_STARTER_CLASS; } ProcessCall process = null; //invoke process factory process = Core.getProcess(className); if (process == null) { pi.setSummary("Failed to create new process instance for " + className, true); return false; } boolean success = false; ClassLoader cl = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(process.getClass().getClassLoader()); process.setProcessUI(processMonitor); success = process.startProcess(ctx, pi, trx); if (success && trx != null && managedTrx) { trx.commit(true); } } catch (Throwable e) { pi.setSummary (Msg.getMsg(Env.getCtx(), "ProcessError") + " " + e.getLocalizedMessage(), true); log.log(Level.SEVERE, pi.getClassName(), e); success = false; return false; } finally { if (trx != null && managedTrx) { if (!success) { trx.rollback(); } trx.close(); trx = null; } Thread.currentThread().setContextClassLoader(cl); } return success; } /** * Start process written in script (javascript, groovy, etc) * @param ctx * @param pi * @param trx * @return true if process completed successfully */ public static boolean startScriptProcess(Properties ctx, ProcessInfo pi, Trx trx) { String msg = null; boolean success = true; try { String cmd = pi.getClassName(); MRule rule = MRule.get(ctx, cmd.substring(MRule.SCRIPT_PREFIX.length())); if (rule == null) { log.log(Level.WARNING, cmd + " not found"); pi.setSummary ("ScriptNotFound", true); return false; } if ( ! (rule.getEventType().equals(MRule.EVENTTYPE_Process) && rule.getRuleType().equals(MRule.RULETYPE_JSR223ScriptingAPIs))) { log.log(Level.WARNING, cmd + " must be of type JSR 223 and event Process"); pi.setSummary ("ScriptNotFound", true); return false; } ScriptEngine engine = rule.getScriptEngine(); if (engine == null) { throw new AdempiereException("Engine not found: " + rule.getEngineName()); } // Window context are W_ // Login context are G_ // Method arguments context are A_ // Parameter context are P_ MRule.setContext(engine, ctx, 0); // no window // now add the method arguments to the engine engine.put(MRule.ARGUMENTS_PREFIX + "Ctx", ctx); if (trx == null) { trx = Trx.get(Trx.createTrxName(pi.getTitle()+"_"+pi.getAD_PInstance_ID()), true); trx.setDisplayName(ProcessUtil.class.getName()+"_startScriptProcess"); } engine.put(MRule.ARGUMENTS_PREFIX + "Trx", trx); engine.put(MRule.ARGUMENTS_PREFIX + "TrxName", trx.getTrxName()); engine.put(MRule.ARGUMENTS_PREFIX + "Record_ID", pi.getRecord_ID()); engine.put(MRule.ARGUMENTS_PREFIX + "AD_Client_ID", pi.getAD_Client_ID()); engine.put(MRule.ARGUMENTS_PREFIX + "AD_User_ID", pi.getAD_User_ID()); engine.put(MRule.ARGUMENTS_PREFIX + "AD_PInstance_ID", pi.getAD_PInstance_ID()); engine.put(MRule.ARGUMENTS_PREFIX + "Table_ID", pi.getTable_ID()); // Add process parameters ProcessInfoParameter[] para = pi.getParameter(); if (para == null) { ProcessInfoUtil.setParameterFromDB(pi); para = pi.getParameter(); } if (para != null) { engine.put(MRule.ARGUMENTS_PREFIX + "Parameter", pi.getParameter()); for (int i = 0; i < para.length; i++) { String name = para[i].getParameterName(); if (para[i].getParameter_To() == null) { Object value = para[i].getParameter(); if (name.endsWith("_ID") && (value instanceof BigDecimal)) engine.put(MRule.PARAMETERS_PREFIX + name, ((BigDecimal)value).intValue()); else engine.put(MRule.PARAMETERS_PREFIX + name, value); } else { Object value1 = para[i].getParameter(); Object value2 = para[i].getParameter_To(); if (name.endsWith("_ID") && (value1 instanceof BigDecimal)) engine.put(MRule.PARAMETERS_PREFIX + name + "1", ((BigDecimal)value1).intValue()); else engine.put(MRule.PARAMETERS_PREFIX + name + "1", value1); if (name.endsWith("_ID") && (value2 instanceof BigDecimal)) engine.put(MRule.PARAMETERS_PREFIX + name + "2", ((BigDecimal)value2).intValue()); else engine.put(MRule.PARAMETERS_PREFIX + name + "2", value2); } } } engine.put(MRule.ARGUMENTS_PREFIX + "ProcessInfo", pi); msg = engine.eval(rule.getScript()).toString(); //transaction should rollback if there are error in process if (msg != null && msg.startsWith("@Error@")) success = false; // Parse Variables msg = Msg.parseTranslation(ctx, msg); pi.setSummary (msg, !success); } catch (Exception e) { pi.setSummary("ScriptError", true); log.log(Level.SEVERE, pi.getClassName(), e); success = false; } if (success) { if (trx != null) { try { trx.commit(true); } catch (Exception e) { log.log(Level.SEVERE, "Commit failed.", e); pi.addSummary("Commit Failed."); pi.setError(true); success = false; } trx.close(); } } else { if (trx != null) { trx.rollback(); trx.close(); } } return success; } /** * Start workflow * @param ctx * @param pi * @param AD_Workflow_ID * @return MWFProcess */ public static MWFProcess startWorkFlow(Properties ctx, ProcessInfo pi, int AD_Workflow_ID) { MWorkflow wf = MWorkflow.get (ctx, AD_Workflow_ID); MWFProcess wfProcess = wf.start(pi, pi.getTransactionName()); if (log.isLoggable(Level.FINE)) log.fine(pi.toString()); return wfProcess; } /** * Start a java process without closing the given transaction. Is used from the workflow engine. * @param ctx * @param pi * @param trx * @return true if process completed successfully */ public static boolean startJavaProcessWithoutTrxClose(Properties ctx, ProcessInfo pi, Trx trx) { return startJavaProcess(ctx, pi, trx, false); } }