/****************************************************************************** * 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.compiere.model; import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.logging.Level; import org.adempiere.base.Core; import org.adempiere.base.event.EventManager; import org.compiere.print.MPrintFormat; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Language; import org.compiere.util.Msg; import org.compiere.util.Util; import org.idempiere.distributed.IMessageService; import org.idempiere.distributed.ITopic; import org.osgi.service.event.Event; /** * Process Instance Model * * @author Jorg Janke * @version $Id: MPInstance.java,v 1.3 2006/07/30 00:58:36 jjanke Exp $ * * @author Teo Sarca, www.arhipac.ro *
  • FR [ 2818478 ] Introduce MPInstance.createParameter helper method * https://sourceforge.net/p/adempiere/feature-requests/756/ */ public class MPInstance extends X_AD_PInstance { /** * generated serial id */ private static final long serialVersionUID = -6414730734415159480L; public static final String ON_RUNNING_JOB_CHANGED_TOPIC = "onRunningJobChanged"; private static CLogger s_log = CLogger.getCLogger (MPInstance.class); /** * UUID based Constructor * @param ctx Context * @param AD_PInstance_UU UUID key * @param trxName Transaction */ public MPInstance(Properties ctx, String AD_PInstance_UU, String trxName) { super(ctx, AD_PInstance_UU, trxName); if (Util.isEmpty(AD_PInstance_UU)) setInitialDefaults(); } /** * Standard Constructor * @param ctx context * @param AD_PInstance_ID instance or 0 * @param ignored no transaction support */ public MPInstance (Properties ctx, int AD_PInstance_ID, String ignored) { super (ctx, AD_PInstance_ID, null); // New Process if (AD_PInstance_ID == 0) setInitialDefaults(); } // MPInstance /** * Set the initial defaults for a new record */ private void setInitialDefaults() { setIsProcessing (false); } /** * Load Constructor * @param ctx context * @param rs result set * @param ignored no transaction support */ public MPInstance (Properties ctx, ResultSet rs, String ignored) { super(ctx, rs, null); } // MPInstance /** * Create Process Instance from Process and create parameters * @param process process * @param Record_ID Record * @deprecated Please use {@link #MPInstance(MProcess, int, int, String)} */ @Deprecated public MPInstance (MProcess process, int Record_ID) { this(process, -1, Record_ID, null); } /** * Create and save new Process Instance from Process and record parameters * @param process process * @param Table_ID * @param Record_ID Record id * @param Record_UU Record uuid */ public MPInstance (MProcess process, int Table_ID, int Record_ID, String Record_UU) { this (process.getCtx(), 0, null); setAD_Process_ID (process.getAD_Process_ID()); setAD_Table_ID(Table_ID); setRecord_ID (Record_ID); setRecord_UU(Record_UU); setAD_User_ID(Env.getAD_User_ID(process.getCtx())); if (!save()) // need to save for parameters throw new IllegalArgumentException ("Cannot Save"); // Set Parameter Base Info MProcessPara[] para = process.getParameters(); for (int i = 0; i < para.length; i++) { MPInstancePara pip = new MPInstancePara (this, para[i].getSeqNo()); pip.setParameterName(para[i].getColumnName()); pip.setInfo(para[i].getName()); pip.saveEx(); } } // MPInstance /** * New Constructor * @param ctx context * @param AD_Process_ID Process ID * @param Record_ID record * @deprecated Please use {@link #MPInstance(Properties, int, int, int, String)} */ @Deprecated public MPInstance (Properties ctx, int AD_Process_ID, int Record_ID) { this(ctx, AD_Process_ID, -1, Record_ID, null); } /** * New Constructor * @param ctx context * @param AD_Process_ID Process ID * @param Table_ID * @param Record_ID record id * @param Record_UU record uuid */ public MPInstance (Properties ctx, int AD_Process_ID, int Table_ID, int Record_ID, String Record_UU) { this(ctx, 0, null); setAD_Process_ID (AD_Process_ID); setAD_Table_ID(Table_ID); setRecord_ID (Record_ID); setRecord_UU(Record_UU); setAD_User_ID(Env.getAD_User_ID(ctx)); setIsProcessing (false); } // MPInstance /** Parameters */ private MPInstancePara[] m_parameters = null; /** * Get Instance Parameters * @return instance parameter array */ public MPInstancePara[] getParameters() { if (m_parameters != null) return m_parameters; //FR: [ 2214883 ] Remove SQL code and Replace for Query - red1 final String whereClause = "AD_PInstance_ID=?"; List list = new Query(getCtx(), I_AD_PInstance_Para.Table_Name, whereClause, null) // @TODO: Review implications of using transaction .setParameters(getAD_PInstance_ID()) .setOrderBy("SeqNo, ParameterName") .list(); // m_parameters = new MPInstancePara[list.size()]; list.toArray(m_parameters); return m_parameters; } // getParameters /** * Get Process Parameters * @return process parameters array */ public MProcessPara[] getProcessParameters() { final String whereClause = "AD_Process_ID=?"; List list = new Query(getCtx(), MProcessPara.Table_Name, whereClause, get_TrxName()) .setParameters(getAD_Process_ID()) .setOnlyActiveRecords(true) .setOrderBy("SeqNo") .list(); // MProcessPara[] processParameters = new MProcessPara[list.size()]; list.toArray(processParameters); return processParameters; } // getParameters /** * Check whether a set of process instance parameters are equal * to the current instance parameters. * @param params array of instance parameters to compare * @return true if the process instance parameters equals to this instance's instance parameters */ public boolean equalParameters(MPInstancePara[] params) { //No parameters if ((getParameters() == null || getParameters().length == 0) && (params == null || params.length == 0)) return true; //Different number of parameters if (getParameters().length != params.length) return false; int comparedParams = 0; for (MPInstancePara instanceParameter : getParameters()) { for (MPInstancePara para : params) { if (instanceParameter.getParameterName().equals(para.getParameterName())) { comparedParams++; if (!instanceParameter.equalParameter(para)) { return false; } break; } } } return comparedParams == getParameters().length; //all the compared parameters have the same name and value } /** Instance Log Entries */ private ArrayList m_log = new ArrayList(); /** * Get Instance Logs * @return array of instance logs */ public MPInstanceLog[] getLog() { // load it from DB m_log.clear(); String sql = "SELECT * FROM AD_PInstance_Log WHERE AD_PInstance_ID=? ORDER BY Log_ID"; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement(sql, null); pstmt.setInt(1, getAD_PInstance_ID()); rs = pstmt.executeQuery(); while (rs.next()) { m_log.add(new MPInstanceLog(rs)); } } catch (Exception e) { log.log(Level.SEVERE, sql, e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } MPInstanceLog[] retValue = new MPInstanceLog[m_log.size()]; m_log.toArray(retValue); return retValue; } // getLog /** * Add instance log * @param P_Date date * @param P_ID id * @param P_Number number * @param P_Msg msg * @return added instance log */ public MPInstanceLog addLog (Timestamp P_Date, int P_ID, BigDecimal P_Number, String P_Msg) { return addLog(P_Date, P_ID, P_Number, P_Msg, 0, 0); } // addLog /** * Add instance log * @param P_Date date * @param P_ID id * @param P_Number number * @param P_Msg msg * @param AD_Table_ID tableID * @param Record_ID recordID * @return added instance log */ public MPInstanceLog addLog (Timestamp P_Date, int P_ID, BigDecimal P_Number, String P_Msg, int AD_Table_ID, int Record_ID) { MPInstanceLog logEntry = new MPInstanceLog (getAD_PInstance_ID(), m_log.size()+1, P_Date, P_ID, P_Number, P_Msg, AD_Table_ID, Record_ID); m_log.add(logEntry); // save it to DB ? return logEntry; } // addLog /** * Set AD_Process_ID. * Throw exception if current role has no permission to run the process. * @param AD_Process_ID process */ public void setAD_Process_ID (int AD_Process_ID) { int AD_Role_ID = Env.getAD_Role_ID(getCtx()); if (AD_Role_ID != 0) { MRole role = MRole.get(getCtx(), AD_Role_ID); Boolean access = role.getProcessAccess(AD_Process_ID); if (access == null || !access.booleanValue()) { MProcess proc = MProcess.get(getCtx(), AD_Process_ID); StringBuilder procMsg = new StringBuilder("["); if (! Language.isBaseLanguage (Env.getAD_Language(getCtx()))) { procMsg.append(proc.get_Translation("Name")).append(" / "); } procMsg.append(proc.getName()).append("]"); throw new IllegalStateException(Msg.getMsg(getCtx(), "CannotAccessProcess", new Object[] {procMsg.toString(), role.getName()})); } } super.setAD_Process_ID (AD_Process_ID); } // setAD_Process_ID /** * Set Record ID. * @param Record_ID record **/ public void setRecord_ID (int Record_ID) { if (Record_ID < 0) { if (log.isLoggable(Level.INFO)) log.info("Set to 0 from " + Record_ID); Record_ID = 0; } set_ValueNoCheck ("Record_ID", Integer.valueOf(Record_ID)); } // setRecord_ID /** * String Representation * @see java.lang.Object#toString() * @return info */ @Override public String toString () { StringBuilder sb = new StringBuilder ("MPInstance[") .append (get_ID()) .append(",OK=").append(isOK()); String msg = getErrorMsg(); if (msg != null && msg.length() > 0) sb.append(msg); sb.append ("]"); return sb.toString (); } // toString /** * Dump Instance Logs to server log */ public void log() { if (log.isLoggable(Level.INFO)) { log.info(toString()); MPInstanceLog[] pil = getLog(); for (int i = 0; i < pil.length; i++) log.info(i + "=" + pil[i]); } } // log /** Result OK = 1 */ public static final int RESULT_OK = 1; /** Result FALSE = 0 */ public static final int RESULT_ERROR = 0; /** * Is it OK * @return true if Result == OK */ public boolean isOK() { return getResult() == RESULT_OK; } // isOK /** * Set Result * @param ok */ public void setResult (boolean ok) { super.setResult (ok ? RESULT_OK : RESULT_ERROR); } // setResult @Override protected boolean afterSave (boolean newRecord, boolean success) { if (!success) return success; // Update Process Statistics if (!newRecord && !isProcessing() && is_ValueChanged("IsProcessing")) { long ms = System.currentTimeMillis() - getCreated().getTime(); int seconds = (int)(ms / 1000); if (seconds < 1) seconds = 1; String updsql = "UPDATE AD_Process SET Statistic_Count=Statistic_Count+1, Statistic_Seconds=Statistic_Seconds+? WHERE AD_Process_ID=?"; int no = DB.executeUpdate(updsql, new Object[] {seconds, getAD_Process_ID()}, true, null); // out of trx if (no == 1) { if (log.isLoggable(Level.FINE)) log.fine("afterSave - Process Statistics updated Sec=" + seconds); } else { log.warning("afterSave - Process Statistics not updated"); } } return success; } // afterSave /** * Create Process Instance Parameter and save to database * @param seqNo parameter sequence# * @param parameterName parameter name * @param value parameter value * @return instance parameter */ public MPInstancePara createParameter(int seqNo, String parameterName, Object value) { MPInstancePara ip = new MPInstancePara(this, seqNo); if (value == null) { ip.setParameter(parameterName, (String)null); } else if (value instanceof BigDecimal) { ip.setParameter(parameterName, (BigDecimal)value); } else if (value instanceof Integer) { ip.setParameter(parameterName, (Integer)value); } else if (value instanceof Timestamp) { ip.setParameter(parameterName, (Timestamp)value); } else if (value instanceof Boolean) { ip.setParameter(parameterName, (Boolean)value); } else { ip.setParameter(parameterName, value.toString()); } // ip.saveEx(); return ip; } @Override public I_AD_Process getAD_Process() throws RuntimeException { return MProcess.get(getAD_Process_ID()); } /** * Publish ON_RUNNING_JOB_CHANGED_TOPIC message to message service.
    * If message service is not available, post as OSGi event instead. * @param AD_User_ID */ public static void publishChangedEvent(int AD_User_ID) { IMessageService service = Core.getMessageService(); if (service != null) { ITopic topic = service.getTopic(ON_RUNNING_JOB_CHANGED_TOPIC); topic.publish(AD_User_ID); } else { postOnChangedEvent(AD_User_ID); } } /** * Post ON_RUNNING_JOB_CHANGED_TOPIC OSGi event. * @param AD_User_ID */ public static void postOnChangedEvent(int AD_User_ID) { Map properties = new HashMap(); properties.put("AD_User_ID", AD_User_ID); Event event = EventManager.newEvent(ON_RUNNING_JOB_CHANGED_TOPIC, properties, true); EventManager.getInstance().postEvent(event); } /** * Get list of process instance via AD_Process_ID and AD_User_ID * @param ctx * @param AD_Process_ID * @param AD_User_ID * @return process instance list */ public static List get(Properties ctx, int AD_Process_ID, int AD_User_ID) { List list = new ArrayList(); List paramsStrAdded = new ArrayList(); List namedInstances = new Query(ctx, Table_Name, "AD_Process_ID=? AND AD_User_ID=? AND Name IS NOT NULL", null) .setClient_ID() .setOnlyActiveRecords(true) .setParameters(AD_Process_ID, AD_User_ID) .setOrderBy("Name") .list(); for (MPInstance namedInstance : namedInstances) { list.add(namedInstance); paramsStrAdded.add(namedInstance.getParamsStr()); } // unnamed instances int lastRunCount = MSysConfig.getIntValue(MSysConfig.LASTRUN_RECORD_COUNT, 5, Env.getAD_Client_ID(ctx)); if (lastRunCount > 0) { int maxLoopCount = 10 * lastRunCount; // using JDBC instead of Query for performance reasons, AD_PInstance can be huge String sql = "SELECT * FROM AD_PInstance " + " WHERE AD_Process_ID=? AND AD_User_ID=? AND IsActive='Y' AND AD_Client_ID=? AND Name IS NULL" + " ORDER BY Created DESC"; PreparedStatement pstmt = null; ResultSet rs = null; int runCount = 0; int loopCount = 0; try { pstmt = DB.prepareStatement(sql, null); pstmt.setFetchSize(lastRunCount); pstmt.setInt(1, AD_Process_ID); pstmt.setInt(2, AD_User_ID); pstmt.setInt(3, Env.getAD_Client_ID(ctx)); rs = pstmt.executeQuery(); while (rs.next()) { loopCount++; MPInstance unnamedInstance = new MPInstance(ctx, rs, null); String paramsStr = unnamedInstance.getParamsStr(); if (! paramsStrAdded.contains(paramsStr)) { unnamedInstance.setName(Msg.getMsg(ctx, "LastRun") + " " + unnamedInstance.getCreated()); list.add(unnamedInstance); paramsStrAdded.add(paramsStr); runCount++; if (runCount == lastRunCount) break; } if (loopCount == maxLoopCount) break; } } catch (Exception e) { s_log.log(Level.SEVERE, "Error while Fetching last run records", e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } } return list; } /** * @return String concatenate all instance parameter values */ private String getParamsStr() { StringBuilder cksum = new StringBuilder(); for (MPInstancePara ip : getParameters()) { cksum.append("(") .append(ip.getParameterName()).append("|") .append(ip.getP_String()).append("|") .append(ip.getP_String_To()).append("|") .append(ip.getP_Number()).append("|") .append(ip.getP_Number_To()).append("|") .append(ip.getP_Date()).append("|") .append(ip.getP_Date_To()).append("|") .append(ip.getInfo()).append("|") .append(ip.getInfo_To()).append("|") .append(")"); } if (getAD_Process().isReport()){ cksum.append(this.getAD_Language_ID()).append("|") .append(this.getAD_PrintFormat_ID()) .append(this.getAD_Language_ID()) .append(this.getReportType()) .append(this.isSummary()); } return cksum.toString(); } /** * Get instance info * @param AD_PInstance_ID * @return {@link PInstanceInfo} * @throws SQLException */ public static PInstanceInfo getPInstanceInfo(int AD_PInstance_ID) throws SQLException { PInstanceInfo info = null; // Get Process Information: Name, Procedure Name, ClassName, IsReport, IsDirectPrint String sql = "SELECT p.Name, p.ProcedureName,p.ClassName, p.AD_Process_ID," // 1..4 + " p.isReport,p.IsDirectPrint,p.AD_ReportView_ID,p.AD_Workflow_ID," // 5..8 + " CASE WHEN COALESCE(p.Statistic_Count,0)=0 THEN 0 ELSE p.Statistic_Seconds/p.Statistic_Count END," // 9 + " p.JasperReport, p.AD_Process_UU " // 10..11 + "FROM AD_Process p" + " INNER JOIN AD_PInstance i ON (p.AD_Process_ID=i.AD_Process_ID) " + "WHERE p.IsActive='Y'" + " AND i.AD_PInstance_ID=?"; if (!Env.isBaseLanguage(Env.getCtx(), "AD_Process")) sql = "SELECT t.Name, p.ProcedureName,p.ClassName, p.AD_Process_ID," // 1..4 + " p.isReport, p.IsDirectPrint,p.AD_ReportView_ID,p.AD_Workflow_ID," // 5..8 + " CASE WHEN COALESCE(p.Statistic_Count,0)=0 THEN 0 ELSE p.Statistic_Seconds/p.Statistic_Count END," // 9 + " p.JasperReport, p.AD_Process_UU " // 10..11 + "FROM AD_Process p" + " INNER JOIN AD_PInstance i ON (p.AD_Process_ID=i.AD_Process_ID) " + " INNER JOIN AD_Process_Trl t ON (p.AD_Process_ID=t.AD_Process_ID" + " AND t.AD_Language='" + Env.getAD_Language(Env.getCtx()) + "') " + "WHERE p.IsActive='Y'" + " AND i.AD_PInstance_ID=?"; // PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, null); pstmt.setInt(1, AD_PInstance_ID); rs = pstmt.executeQuery(); if (rs.next()) { info = new PInstanceInfo(); info.name = rs.getString(1); info.procedureName = rs.getString(2); info.className = rs.getString(3); info.AD_Process_ID = rs.getInt(4); info.AD_Process_UU = rs.getString(11); // Report if ("Y".equals(rs.getString(5))) { info.isReport = true; } if ("Y".equals(rs.getString(6))) { info.isDirectPrint = true; } info.AD_ReportView_ID = rs.getInt(7); info.AD_Workflow_ID = rs.getInt(8); // info.estimate = rs.getInt(9); info.jasperReport = rs.getString(10); } } finally { DB.close(rs, pstmt); } return info; } /** * Instance info record class with fields from AD_PInstance+AD_Process * @author hengsin */ public static class PInstanceInfo { public int AD_Process_ID; public String AD_Process_UU; public int AD_ReportView_ID = 0; public int AD_Workflow_ID = 0; //translated name for current env context public String name; public String className; public String procedureName = ""; public String jasperReport = ""; public boolean isReport = false; public boolean isDirectPrint = false; public int estimate; } @Override protected boolean beforeSave (boolean newRecord) { // Set AD_Session_ID from environment context if (newRecord) { int sessionId = Env.getContextAsInt(Env.getCtx(), Env.AD_SESSION_ID); if (sessionId > 0) setAD_Session_ID(sessionId); } return true; } // beforeSave /** * Set AD_PrintFormat_ID if empty, AD_Language_ID if empty and save the record. * @param format */ public void updatePrintFormatAndLanguageIfEmpty(MPrintFormat format) { if(getAD_PrintFormat_ID() <= 0 && format.getAD_PrintFormat_ID() > 0) { setAD_PrintFormat_ID(format.getAD_PrintFormat_ID()); saveEx(); } if(getAD_Language_ID() <= 0 && format.getLanguage() != null) { setAD_Language_ID(MLanguage.get(Env.getCtx(), format.getLanguage()).getAD_Language_ID()); saveEx(); } } } // MPInstance