From 4029fbd81cd5615de0c99848b012326379b7e2a7 Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Wed, 19 Aug 2015 22:12:11 -0500 Subject: [PATCH] IDEMPIERE-2771 Improve 2pack tracing for errors / implement mail notifier for 2packs --- .../oracle/201508192155_IDEMPIERE-2771.sql | 11 ++ .../201508192155_IDEMPIERE-2771.sql | 8 + .../src/org/compiere/model/MSysConfig.java | 3 +- .../src/org/compiere/util/EMail.java | 11 +- .../handler/GenericPOElementHandler.java | 6 + .../handler/SQLStatementElementHandler.java | 9 +- .../pipo/srv/PipoDictionaryService.java | 4 + .../pipo2/AbstractElementHandler.java | 8 + .../src/org/adempiere/pipo2/PackIn.java | 9 +- .../org/adempiere/pipo2/PackInHandler.java | 16 +- .../org/adempiere/pipo2/PackInNotifier.java | 169 ++++++++++++++++++ .../org/adempiere/pipo2/PackInProcess.java | 24 ++- 12 files changed, 258 insertions(+), 20 deletions(-) create mode 100644 migration/i2.1/oracle/201508192155_IDEMPIERE-2771.sql create mode 100644 migration/i2.1/postgresql/201508192155_IDEMPIERE-2771.sql create mode 100644 org.adempiere.pipo/src/org/adempiere/pipo2/PackInNotifier.java diff --git a/migration/i2.1/oracle/201508192155_IDEMPIERE-2771.sql b/migration/i2.1/oracle/201508192155_IDEMPIERE-2771.sql new file mode 100644 index 0000000000..2ebbf760fa --- /dev/null +++ b/migration/i2.1/oracle/201508192155_IDEMPIERE-2771.sql @@ -0,0 +1,11 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IDEMPIERE-2771 Improve 2pack tracing for errors +-- Aug 19, 2015 9:55:02 PM COT +INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200067,0,0,TO_DATE('2015-08-19 21:55:02','YYYY-MM-DD HH24:MI:SS'),TO_DATE('2015-08-19 21:55:02','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','EMAIL_NOTIFY_2PACK',' ','EMail to get 2Pack Notifications','D','C','12ab33a5-4129-4d5b-9066-dbdbc20df816') +; + +SELECT register_migration_script('201508192155_IDEMPIERE-2771.sql') FROM dual +; + diff --git a/migration/i2.1/postgresql/201508192155_IDEMPIERE-2771.sql b/migration/i2.1/postgresql/201508192155_IDEMPIERE-2771.sql new file mode 100644 index 0000000000..92b53c3968 --- /dev/null +++ b/migration/i2.1/postgresql/201508192155_IDEMPIERE-2771.sql @@ -0,0 +1,8 @@ +-- IDEMPIERE-2771 Improve 2pack tracing for errors +-- Aug 19, 2015 9:55:02 PM COT +INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200067,0,0,TO_TIMESTAMP('2015-08-19 21:55:02','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2015-08-19 21:55:02','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','EMAIL_NOTIFY_2PACK',' ','EMail to get 2Pack Notifications','D','C','12ab33a5-4129-4d5b-9066-dbdbc20df816') +; + +SELECT register_migration_script('201508192155_IDEMPIERE-2771.sql') FROM dual +; + diff --git a/org.adempiere.base/src/org/compiere/model/MSysConfig.java b/org.adempiere.base/src/org/compiere/model/MSysConfig.java index e26b2283fd..2483f91a81 100644 --- a/org.adempiere.base/src/org/compiere/model/MSysConfig.java +++ b/org.adempiere.base/src/org/compiere/model/MSysConfig.java @@ -42,7 +42,7 @@ public class MSysConfig extends X_AD_SysConfig /** * */ - private static final long serialVersionUID = -2870394087507976203L; + private static final long serialVersionUID = 2300170888492939423L; public static final String ADDRESS_VALIDATION = "ADDRESS_VALIDATION"; public static final String ALERT_SEND_ATTACHMENT_AS_XLS = "ALERT_SEND_ATTACHMENT_AS_XLS"; @@ -79,6 +79,7 @@ public class MSysConfig extends X_AD_SysConfig public static final String DOCACTIONBUTTON_SHOWACTIONNAME = "DOCACTIONBUTTON_SHOWACTIONNAME"; public static final String DPVIEWS_SHOWINFOACCOUNT = "DPViews_ShowInfoAccount"; public static final String DPVIEWS_SHOWINFOSCHEDULE = "DPViews_ShowInfoSchedule"; + public static final String EMAIL_NOTIFY_2PACK = "EMAIL_NOTIFY_2PACK"; public static final String ENABLE_PAYMENTBOX_BUTTON = "ENABLE_PAYMENTBOX_BUTTON"; public static final String GRIDTABLE_LOAD_TIMEOUT_IN_SECONDS = "GRIDTABLE_LOAD_TIMEOUT_IN_SECONDS"; public static final String HTML_REPORT_THEME = "HTML_REPORT_THEME"; diff --git a/org.adempiere.base/src/org/compiere/util/EMail.java b/org.adempiere.base/src/org/compiere/util/EMail.java index b0241e12ef..cfa7b720c2 100644 --- a/org.adempiere.base/src/org/compiere/util/EMail.java +++ b/org.adempiere.base/src/org/compiere/util/EMail.java @@ -48,6 +48,7 @@ import javax.mail.internet.MimeMultipart; import org.compiere.model.MClient; import org.compiere.model.MSysConfig; + import com.sun.mail.smtp.SMTPMessage; /** @@ -72,7 +73,7 @@ public final class EMail implements Serializable /** * */ - private static final long serialVersionUID = -5857825644737211294L; + private static final long serialVersionUID = 8117458371229316972L; //use in server bean public final static String HTML_MAIL_MARKER = "ContentType=text/html;"; @@ -1200,4 +1201,12 @@ public final class EMail implements Serializable email.send(); } // main + public void setHeader(String name, String value) { + try { + m_msg.setHeader(name, value); + } catch (MessagingException e) { + log.log(Level.WARNING, m_msg.toString(), e); + } + } + } // EMail diff --git a/org.adempiere.pipo.handlers/src/org/adempiere/pipo2/handler/GenericPOElementHandler.java b/org.adempiere.pipo.handlers/src/org/adempiere/pipo2/handler/GenericPOElementHandler.java index b70ced694f..fbf14a0273 100644 --- a/org.adempiere.pipo.handlers/src/org/adempiere/pipo2/handler/GenericPOElementHandler.java +++ b/org.adempiere.pipo.handlers/src/org/adempiere/pipo2/handler/GenericPOElementHandler.java @@ -46,6 +46,7 @@ import org.compiere.model.MColumn; import org.compiere.model.MRole; import org.compiere.model.MTable; import org.compiere.model.PO; +import org.compiere.model.X_AD_Package_Imp_Detail; import org.compiere.util.DB; import org.compiere.util.Env; import org.xml.sax.SAXException; @@ -95,8 +96,13 @@ public class GenericPOElementHandler extends AbstractElementHandler { element.unresolved = notfounds.toString(); return; } + String action = po.is_new() ? "New" : "Update"; po.saveEx(); element.recordId = po.get_ID(); + + X_AD_Package_Imp_Detail impDetail = createImportDetail(ctx, element.qName, po.get_TableName(), po.get_Table_ID()); + logImportDetail(ctx, impDetail, 1, po.toString(), element.recordId, action); + if ( I_AD_Window.Table_Name.equals(tableName) || I_AD_Process.Table_Name.equals(tableName) || I_AD_Role.Table_Name.equals(tableName) diff --git a/org.adempiere.pipo.handlers/src/org/adempiere/pipo2/handler/SQLStatementElementHandler.java b/org.adempiere.pipo.handlers/src/org/adempiere/pipo2/handler/SQLStatementElementHandler.java index 517a4cbba8..cf88b9cec9 100644 --- a/org.adempiere.pipo.handlers/src/org/adempiere/pipo2/handler/SQLStatementElementHandler.java +++ b/org.adempiere.pipo.handlers/src/org/adempiere/pipo2/handler/SQLStatementElementHandler.java @@ -51,6 +51,7 @@ public class SQLStatementElementHandler extends AbstractElementHandler { Savepoint savepoint = null; PreparedStatement pstmt = null; X_AD_Package_Imp_Detail impDetail = null; + impDetail = createImportDetail(ctx, element.qName, "", 0); try { // NOTE Postgres needs to commit DDL statements // add a SQL command just with COMMIT if you want to simulate the Oracle behavior (commit on DDL) @@ -86,6 +87,8 @@ public class SQLStatementElementHandler extends AbstractElementHandler { stmt = null; } } + logImportDetail (ctx, impDetail, 1, "SQLStatement",1,"Execute"); + ctx.packIn.getNotifier().addSuccessLine("-> " + sql); } catch (Exception e) { // rollback immediately on exception to avoid a wrong SQL stop the whole process if (savepoint != null) @@ -101,6 +104,9 @@ public class SQLStatementElementHandler extends AbstractElementHandler { } savepoint = null; } + ctx.packIn.getNotifier().addFailureLine("SQL statement failed but ignored, error (" + e.getLocalizedMessage() + "): " + sql); + logImportDetail (ctx, impDetail, 0, "SQLStatement",1,"Execute"); + ctx.packIn.getNotifier().addFailureLine("-> " + sql); log.log(Level.SEVERE,"SQLSatement", e); } finally { DB.close(pstmt); @@ -118,9 +124,6 @@ public class SQLStatementElementHandler extends AbstractElementHandler { } } } - impDetail = createImportDetail(ctx, element.qName, "", - 0); - logImportDetail (ctx, impDetail, 1, "SQLStatement",1,"Execute"); } public void endElement(PIPOContext ctx, Element element) throws SAXException { diff --git a/org.adempiere.pipo/src/org/adempiere/pipo/srv/PipoDictionaryService.java b/org.adempiere.pipo/src/org/adempiere/pipo/srv/PipoDictionaryService.java index a1f6762dbf..74a54130fe 100644 --- a/org.adempiere.pipo/src/org/adempiere/pipo/srv/PipoDictionaryService.java +++ b/org.adempiere.pipo/src/org/adempiere/pipo/srv/PipoDictionaryService.java @@ -78,6 +78,8 @@ public class PipoDictionaryService implements IDictionaryService { packIn.setPackageVersion(packageVersion); packIn.setUpdateDictionary(false); + packIn.getNotifier().setFileName(packageFile.getName()); + packIn.getNotifier().setPluginName(context.getBundle().getSymbolicName() + " v" + packageVersion); adPackageImp = new X_AD_Package_Imp_Proc(Env.getCtx(), 0, null); if (logger.isLoggable(Level.INFO)) logger.info("zipFilepath->" + packageFile); @@ -104,6 +106,7 @@ public class PipoDictionaryService implements IDictionaryService { if (logger.isLoggable(Level.INFO)) logger.info("commit " + trxName); } catch (Exception e) { adPackageImp.setP_Msg(e.getLocalizedMessage()); + packIn.getNotifier().addStatusLine(e.getLocalizedMessage()); logger.log(Level.SEVERE, "importXML:", e); throw e; } finally { @@ -122,6 +125,7 @@ public class PipoDictionaryService implements IDictionaryService { attachment.save(); // ignoring exceptions } } + packIn.getNotifier().notifyRecipient(); } } diff --git a/org.adempiere.pipo/src/org/adempiere/pipo2/AbstractElementHandler.java b/org.adempiere.pipo/src/org/adempiere/pipo2/AbstractElementHandler.java index ca0e3ecb33..246975a92b 100644 --- a/org.adempiere.pipo/src/org/adempiere/pipo2/AbstractElementHandler.java +++ b/org.adempiere.pipo/src/org/adempiere/pipo2/AbstractElementHandler.java @@ -100,6 +100,14 @@ public abstract class AbstractElementHandler implements ElementHandler { detail.setSuccess(result); detail.setRecord_ID(objectID); ctx.packIn.addImportDetail(detail); + StringBuilder msg = new StringBuilder(action).append(" "); + if (detail.getTableName() != null) + msg.append(detail.getTableName()); + msg.append("=").append(objectName).append("[").append(objectID).append("]"); + if (success == 1) + ctx.packIn.getNotifier().addSuccessLine(msg.toString()); + else + ctx.packIn.getNotifier().addFailureLine(msg.toString()); } /** diff --git a/org.adempiere.pipo/src/org/adempiere/pipo2/PackIn.java b/org.adempiere.pipo/src/org/adempiere/pipo2/PackIn.java index e67b10065c..ff2c95685c 100644 --- a/org.adempiere.pipo/src/org/adempiere/pipo2/PackIn.java +++ b/org.adempiere.pipo/src/org/adempiere/pipo2/PackIn.java @@ -61,6 +61,7 @@ public class PackIn { private Map columnCache = new HashMap(); private String packageName = null; private String packageVersion = null; + private PackInNotifier notifier = new PackInNotifier(this); private X_AD_Package_Imp_Proc packinProc; private List importDetails; @@ -176,7 +177,9 @@ public class PackIn { log.info(msg); if (handler.getUnresolvedCount() > 0) handler.dumpUnresolvedElements(); - return "Processed="+handler.getElementsProcessed()+" Un-Resolved="+handler.getUnresolvedCount(); + msg = "Processed="+handler.getElementsProcessed()+" Un-Resolved="+handler.getUnresolvedCount(); + getNotifier().addStatusLine(msg); + return msg; } catch (Exception e) { log.log(Level.SEVERE, "importXML:", e); throw new RuntimeException(e.getLocalizedMessage(), e); @@ -288,6 +291,10 @@ public class PackIn { this.packageVersion = packageVersion; } + public PackInNotifier getNotifier() { + return notifier; + } + public X_AD_Package_Imp_Proc getAD_Package_Imp_Proc() { if (packinProc.getAD_Package_Imp_Proc_ID() == 0) packinProc.saveEx(); // we need the ID to set diff --git a/org.adempiere.pipo/src/org/adempiere/pipo2/PackInHandler.java b/org.adempiere.pipo/src/org/adempiere/pipo2/PackInHandler.java index 269a3a4380..5b2281437f 100644 --- a/org.adempiere.pipo/src/org/adempiere/pipo2/PackInHandler.java +++ b/org.adempiere.pipo/src/org/adempiere/pipo2/PackInHandler.java @@ -306,14 +306,13 @@ public class PackInHandler extends DefaultHandler { updateRoleAccess(); - if (!packageStatus.equals("Completed with errors")) { - if (getUnresolvedCount() > 0) { - packageStatus = "Completed - unresolved"; - } else { - packageStatus = "Completed successfully"; - packIn.setSuccess(true); - } + if (getUnresolvedCount() > 0) { + packageStatus = "Completed - unresolved"; + } else { + packageStatus = "Completed successfully"; + packIn.setSuccess(true); } + packIn.getNotifier().addStatusLine(packageStatus); //Update package history log with package status packageImp.setPK_Status(packageStatus); @@ -334,12 +333,14 @@ public class PackInHandler extends DefaultHandler { processElement(e); } catch (RuntimeException re) { packageStatus = "Import Failed"; + packIn.getNotifier().addStatusLine(packageStatus); //Update package history log with package status packageImp.setPK_Status(packageStatus); packageImp.saveEx(); throw re; } catch (SAXException se) { packageStatus = "Import Failed"; + packIn.getNotifier().addStatusLine(packageStatus); //Update package history log with package status packageImp.setPK_Status(packageStatus); packageImp.saveEx(); @@ -487,6 +488,7 @@ public class PackInHandler extends DefaultHandler { if (e.unresolved != null && e.unresolved.length() > 0) s.append(" unresolved ").append(e.unresolved); log.warning(s.toString()); + packIn.getNotifier().addFailureLine(s.toString()); } } } diff --git a/org.adempiere.pipo/src/org/adempiere/pipo2/PackInNotifier.java b/org.adempiere.pipo/src/org/adempiere/pipo2/PackInNotifier.java new file mode 100644 index 0000000000..2edfebd49b --- /dev/null +++ b/org.adempiere.pipo/src/org/adempiere/pipo2/PackInNotifier.java @@ -0,0 +1,169 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Trek Global * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* 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., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +* * +* Developer: * +* - Carlos Ruiz - globalqss * +**********************************************************************/ + +package org.adempiere.pipo2; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.compiere.model.MClient; +import org.compiere.model.MSysConfig; +import org.compiere.util.EMail; +import org.compiere.util.Env; +import org.compiere.util.KeyNamePair; + +/** + * PackIn Notifier + * + * @author Carlos Ruiz - globalqss - sponsored by Trek Global + */ + +public class PackInNotifier { + + private String fileName; + private String pluginName; + private List knpLines = new ArrayList(); + private PackIn packIn; + + public PackInNotifier(PackIn packIn) { + this.packIn = packIn; + } + + private static final int LEVEL_STATUS = 0; + private static final int LEVEL_FAILURE = 1; + private static final int LEVEL_SUCCESS = 2; + + public void addStatusLine(String msg) { + addLine(LEVEL_STATUS, msg); + } + + public void addFailureLine(String msg) { + addLine(LEVEL_FAILURE, msg); + } + + public void addSuccessLine(String msg) { + addLine(LEVEL_SUCCESS, msg); + } + + public void addLine(int level, String msg) { + knpLines.add(new KeyNamePair(level, msg)); + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getPluginName() { + return pluginName; + } + + public void setPluginName(String pluginName) { + this.pluginName = pluginName; + } + + public void notifyRecipient() { + // get list from current tenant + String emailList = MSysConfig.getValue(MSysConfig.EMAIL_NOTIFY_2PACK, "", Env.getAD_Client_ID(Env.getCtx())).trim(); + String emailSys = MSysConfig.getValue(MSysConfig.EMAIL_NOTIFY_2PACK, "", 0).trim(); + if (emailSys.length() > 0 && !emailList.equals(emailSys)) + emailList += "," + emailSys; + if (emailList.length() == 0) + return; + + // Compose Subject + StringBuilder subject = new StringBuilder("*"); + String status; + if (packIn.isSuccess()) { + status = "Success"; + } else { + status = "Failure"; + } + subject.append(status).append("* Result for PackIn ").append(getFileName()); + if (getPluginName() != null) { + subject.append(" from ").append(getPluginName()); + } + + // Body + StringBuilder message = new StringBuilder(); + message.append("===========================\n"); + message.append("Packin File: ").append(getFileName()).append("\n"); + if (getPluginName() != null) + message.append("Plugin: ").append(getPluginName()).append("\n"); + MClient client = MClient.get(Env.getCtx()); + message.append("Executed on: ").append(client.getName()).append("\n"); + message.append("Status: ").append(status).append("\n"); + for (String line : getLines(LEVEL_STATUS)) { + message.append(line).append("\n"); + } + message.append("===========================\n"); + + // --> if failed: + List fLines = getLines(LEVEL_FAILURE); + if (fLines.size() > 0) { + message.append("Failed Objects:").append("\n"); + for (String line : fLines) { + message.append(line).append("\n"); + } + message.append("===========================\n"); + } + + List sLines = getLines(LEVEL_SUCCESS); + if (sLines.size() > 0) { + message.append("Successful Objects:").append("\n"); + for (String line : sLines) { + message.append(line).append("\n"); + } + message.append("===========================\n"); + } + + StringTokenizer st = new StringTokenizer(emailList, " ,;", false); + String to = st.nextToken(); + EMail email = client.createEMail(null, to, subject.toString(), message.toString()); + if (email != null) + { + if (!packIn.isSuccess()) + email.setHeader("X-Priority", "1"); + while (st.hasMoreTokens()) + email.addTo(st.nextToken()); + status = email.send(); + } + } + + private List getLines(int levelStatus) { + List lines = new ArrayList(); + for (KeyNamePair knp : knpLines) { + if (knp.getKey() == levelStatus) { + lines.add(knp.getName()); + } + } + return lines; + } + +} diff --git a/org.adempiere.pipo/src/org/adempiere/pipo2/PackInProcess.java b/org.adempiere.pipo/src/org/adempiere/pipo2/PackInProcess.java index 011fe0d1e3..26f8d735ce 100644 --- a/org.adempiere.pipo/src/org/adempiere/pipo2/PackInProcess.java +++ b/org.adempiere.pipo/src/org/adempiere/pipo2/PackInProcess.java @@ -75,8 +75,7 @@ public class PackInProcess extends SvrProcess { */ protected String doIt() throws Exception { - X_AD_Package_Imp_Proc adPackageImp = new X_AD_Package_Imp_Proc(getCtx(), - p_PackIn_ID, get_TrxName()); + X_AD_Package_Imp_Proc adPackageImp = new X_AD_Package_Imp_Proc(getCtx(), p_PackIn_ID, null); // out of trx // Create Target directory if required String packageDirectory = adPackageImp.getAD_Package_Dir(); @@ -134,14 +133,25 @@ public class PackInProcess extends SvrProcess { packIn.setPackageName(packageName); packIn.setPackageVersion(packageVersion); packIn.setUpdateDictionary(m_UpdateDictionary); + packIn.getNotifier().setFileName(zipFilepath.getName()); packIn.setAD_Package_Imp_Proc(adPackageImp); // call XML Handler - String msg = packIn.importXML(dict_file, getCtx(), get_TrxName()); - adPackageImp.setDateProcessed(new Timestamp(System.currentTimeMillis())); - adPackageImp.setP_Msg(msg); - adPackageImp.saveEx(); - + String msg; + try { + msg = packIn.importXML(dict_file, getCtx(), get_TrxName()); + adPackageImp.setDateProcessed(new Timestamp(System.currentTimeMillis())); + adPackageImp.setP_Msg(msg); + adPackageImp.saveEx(); + } catch (Exception e) { + adPackageImp.setP_Msg(e.getLocalizedMessage()); + packIn.getNotifier().addStatusLine(e.getLocalizedMessage()); + log.log(Level.SEVERE, "importXML:", e); + throw e; + } finally { + adPackageImp.save(); // ignoring exceptions + packIn.getNotifier().notifyRecipient(); + } return msg; } // doIt } // PackInProcess