From 89c00b7adac4dadef48ad90382fc7b5494c3d7c3 Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Fri, 16 Mar 2018 17:49:25 -0300 Subject: [PATCH] IDEMPIERE-3660 Improve Packin - allow application on all tenants --- .../oracle/201803151613_IDEMPIERE-3660.sql | 72 +++++++ .../201803151613_IDEMPIERE-3660.sql | 69 +++++++ .../adempiere/base/IDictionaryService.java | 5 + org.adempiere.pipo/plugin.xml | 8 + .../pipo/srv/PipoDictionaryService.java | 19 +- .../src/org/adempiere/pipo2/PackInFolder.java | 90 +++++++++ .../org/adempiere/pipo2/PackInProcess.java | 3 +- .../META-INF/MANIFEST.MF | 1 + .../plugin/utils/AbstractActivator.java | 76 ++++++- .../utils/PackInApplicationActivator.java | 187 +++++++++++++----- .../adempiere/webui/apps/ProcessDialog.java | 2 +- .../css/fragment/parameter-process.css.dsp | 2 +- 12 files changed, 466 insertions(+), 68 deletions(-) create mode 100644 migration/i5.1/oracle/201803151613_IDEMPIERE-3660.sql create mode 100644 migration/i5.1/postgresql/201803151613_IDEMPIERE-3660.sql create mode 100644 org.adempiere.pipo/src/org/adempiere/pipo2/PackInFolder.java diff --git a/migration/i5.1/oracle/201803151613_IDEMPIERE-3660.sql b/migration/i5.1/oracle/201803151613_IDEMPIERE-3660.sql new file mode 100644 index 0000000000..150cf8436b --- /dev/null +++ b/migration/i5.1/oracle/201803151613_IDEMPIERE-3660.sql @@ -0,0 +1,72 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IDEMPIERE-3660 Improve Packin - allow application on all tenants +-- Mar 15, 2018 4:09:45 PM BRT +INSERT INTO AD_Process (AD_Process_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,Name,Description,Help,IsReport,Value,IsDirectPrint,Classname,AccessLevel,EntityType,Statistic_Count,Statistic_Seconds,IsBetaFunctionality,IsServerProcess,ShowHelp,CopyFromProcess,AD_Process_UU) VALUES (200099,0,0,'Y',TO_DATE('2018-03-15 16:09:44','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-03-15 16:09:44','YYYY-MM-DD HH24:MI:SS'),100,'Apply Pack In from Folder','Apply all zip files from a folder following the rules and conventions of Automatic Pack In process','This process apply all the pack in files found in a folder recursively - ordered by timestamp. +The filename of the pack in files is important, it must follow the following convention: +[Timestamp]_[ClientValue][_AdditionalInformation].zip +* Timestamp: must be in the format yyyymmddHHMM +* ClientValue: case sensitive and compared against AD_Client.Value to find the tenant where the pack in must be applied +* AdditionalInformation: Any additional information to identify the zip file +NOTE that AD_Client.Value must not contain underscore in order for this process to work. +There is a special case for ClientValue, if the ClientValue is ALL-CLIENTS then the 2pack is intended to be applied in all active non-system clients. +If there is a need to apply initially in a seed tenant then the ClientValue must take the form ALL-CLIENTS-Seed. +For example: +201803151607_ALL-CLIENTS-GardenWorld_Test123.zip','N','ApplyPackInFolder','N','org.adempiere.pipo2.PackInFolder','4','D',0,0,'N','N','Y','N','670d19a4-8072-46c8-9e66-3cb3008d18f3') +; + +-- Mar 15, 2018 4:10:09 PM BRT +INSERT INTO AD_Menu (AD_Menu_ID,Name,Description,Action,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsSummary,AD_Process_ID,IsSOTrx,IsReadOnly,EntityType,IsCentrallyMaintained,AD_Menu_UU) VALUES (200153,'Apply Pack In from Folder','Apply all zip files from a folder following the rules and conventions of Automatic Pack In process','P',0,0,'Y',TO_DATE('2018-03-15 16:10:09','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-03-15 16:10:09','YYYY-MM-DD HH24:MI:SS'),100,'N',200099,'Y','N','D','Y','dee67807-ff1c-45c8-94bd-5c025c3ec902') +; + +-- Mar 15, 2018 4:10:09 PM BRT +INSERT INTO AD_TreeNodeMM (AD_Client_ID,AD_Org_ID, IsActive,Created,CreatedBy,Updated,UpdatedBy, AD_Tree_ID, Node_ID, Parent_ID, SeqNo, AD_TreeNodeMM_UU) SELECT t.AD_Client_ID, 0, 'Y', SysDate, 100, SysDate, 100,t.AD_Tree_ID, 200153, 0, 999, Generate_UUID() FROM AD_Tree t WHERE t.AD_Client_ID=0 AND t.IsActive='Y' AND t.IsAllNodes='Y' AND t.TreeType='MM' AND NOT EXISTS (SELECT * FROM AD_TreeNodeMM e WHERE e.AD_Tree_ID=t.AD_Tree_ID AND Node_ID=200153) +; + +-- Mar 15, 2018 4:10:13 PM BRT +UPDATE AD_TreeNodeMM SET Parent_ID=50001, SeqNo=0, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=50004 +; + +-- Mar 15, 2018 4:10:13 PM BRT +UPDATE AD_TreeNodeMM SET Parent_ID=50001, SeqNo=1, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=50006 +; + +-- Mar 15, 2018 4:10:13 PM BRT +UPDATE AD_TreeNodeMM SET Parent_ID=50001, SeqNo=2, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=50002 +; + +-- Mar 15, 2018 4:10:13 PM BRT +UPDATE AD_TreeNodeMM SET Parent_ID=50001, SeqNo=3, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=50003 +; + +-- Mar 15, 2018 4:10:13 PM BRT +UPDATE AD_TreeNodeMM SET Parent_ID=50001, SeqNo=4, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=200153 +; + +-- Mar 15, 2018 4:12:47 PM BRT +INSERT INTO AD_Process_Para (AD_Process_Para_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,Name,Description,AD_Process_ID,SeqNo,AD_Reference_ID,IsRange,FieldLength,IsMandatory,ColumnName,IsCentrallyMaintained,EntityType,AD_Element_ID,AD_Process_Para_UU,IsEncrypted) VALUES (200226,0,0,'Y',TO_DATE('2018-03-15 16:12:46','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-03-15 16:12:46','YYYY-MM-DD HH24:MI:SS'),100,'Folder','Path on the server where the pack in files are located',200099,10,10,'N',1000,'Y','Folder','N','D',3012,'e0c4b099-68ba-48fe-b629-043befec7bbe','N') +; + +-- Mar 15, 2018 4:43:24 PM BRT +UPDATE AD_Process SET Help='

This process apply all the pack in files found in a folder recursively - ordered by timestamp.
+The filename of the pack in files is important, it must follow the following convention:
+[Timestamp]_[ClientValue][_AdditionalInformation].zip

+ + + +

NOTE that AD_Client.Value must not contain underscore in order for this process to work.
+There is a special case for ClientValue, if the ClientValue is ALL-CLIENTS then the 2pack is intended to be applied in all active non-system clients.
+If there is a need to apply initially in a seed tenant then the ClientValue must take the form ALL-CLIENTS-Seed.
+For example:
+201803151607_ALL-CLIENTS-GardenWorld_Test123.zip

+',Updated=TO_DATE('2018-03-15 16:43:24','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_ID=200099 +; + +SELECT register_migration_script('201803151613_IDEMPIERE-3660.sql') FROM dual +; + diff --git a/migration/i5.1/postgresql/201803151613_IDEMPIERE-3660.sql b/migration/i5.1/postgresql/201803151613_IDEMPIERE-3660.sql new file mode 100644 index 0000000000..6cfa8458eb --- /dev/null +++ b/migration/i5.1/postgresql/201803151613_IDEMPIERE-3660.sql @@ -0,0 +1,69 @@ +-- IDEMPIERE-3660 Improve Packin - allow application on all tenants +-- Mar 15, 2018 4:09:45 PM BRT +INSERT INTO AD_Process (AD_Process_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,Name,Description,Help,IsReport,Value,IsDirectPrint,Classname,AccessLevel,EntityType,Statistic_Count,Statistic_Seconds,IsBetaFunctionality,IsServerProcess,ShowHelp,CopyFromProcess,AD_Process_UU) VALUES (200099,0,0,'Y',TO_TIMESTAMP('2018-03-15 16:09:44','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-03-15 16:09:44','YYYY-MM-DD HH24:MI:SS'),100,'Apply Pack In from Folder','Apply all zip files from a folder following the rules and conventions of Automatic Pack In process','This process apply all the pack in files found in a folder recursively - ordered by timestamp. +The filename of the pack in files is important, it must follow the following convention: +[Timestamp]_[ClientValue][_AdditionalInformation].zip +* Timestamp: must be in the format yyyymmddHHMM +* ClientValue: case sensitive and compared against AD_Client.Value to find the tenant where the pack in must be applied +* AdditionalInformation: Any additional information to identify the zip file +NOTE that AD_Client.Value must not contain underscore in order for this process to work. +There is a special case for ClientValue, if the ClientValue is ALL-CLIENTS then the 2pack is intended to be applied in all active non-system clients. +If there is a need to apply initially in a seed tenant then the ClientValue must take the form ALL-CLIENTS-Seed. +For example: +201803151607_ALL-CLIENTS-GardenWorld_Test123.zip','N','ApplyPackInFolder','N','org.adempiere.pipo2.PackInFolder','4','D',0,0,'N','N','Y','N','670d19a4-8072-46c8-9e66-3cb3008d18f3') +; + +-- Mar 15, 2018 4:10:09 PM BRT +INSERT INTO AD_Menu (AD_Menu_ID,Name,Description,"action",AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsSummary,AD_Process_ID,IsSOTrx,IsReadOnly,EntityType,IsCentrallyMaintained,AD_Menu_UU) VALUES (200153,'Apply Pack In from Folder','Apply all zip files from a folder following the rules and conventions of Automatic Pack In process','P',0,0,'Y',TO_TIMESTAMP('2018-03-15 16:10:09','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-03-15 16:10:09','YYYY-MM-DD HH24:MI:SS'),100,'N',200099,'Y','N','D','Y','dee67807-ff1c-45c8-94bd-5c025c3ec902') +; + +-- Mar 15, 2018 4:10:09 PM BRT +INSERT INTO AD_TreeNodeMM (AD_Client_ID,AD_Org_ID, IsActive,Created,CreatedBy,Updated,UpdatedBy, AD_Tree_ID, Node_ID, Parent_ID, SeqNo, AD_TreeNodeMM_UU) SELECT t.AD_Client_ID, 0, 'Y', statement_timestamp(), 100, statement_timestamp(), 100,t.AD_Tree_ID, 200153, 0, 999, Generate_UUID() FROM AD_Tree t WHERE t.AD_Client_ID=0 AND t.IsActive='Y' AND t.IsAllNodes='Y' AND t.TreeType='MM' AND NOT EXISTS (SELECT * FROM AD_TreeNodeMM e WHERE e.AD_Tree_ID=t.AD_Tree_ID AND Node_ID=200153) +; + +-- Mar 15, 2018 4:10:13 PM BRT +UPDATE AD_TreeNodeMM SET Parent_ID=50001, SeqNo=0, Updated=statement_timestamp() WHERE AD_Tree_ID=10 AND Node_ID=50004 +; + +-- Mar 15, 2018 4:10:13 PM BRT +UPDATE AD_TreeNodeMM SET Parent_ID=50001, SeqNo=1, Updated=statement_timestamp() WHERE AD_Tree_ID=10 AND Node_ID=50006 +; + +-- Mar 15, 2018 4:10:13 PM BRT +UPDATE AD_TreeNodeMM SET Parent_ID=50001, SeqNo=2, Updated=statement_timestamp() WHERE AD_Tree_ID=10 AND Node_ID=50002 +; + +-- Mar 15, 2018 4:10:13 PM BRT +UPDATE AD_TreeNodeMM SET Parent_ID=50001, SeqNo=3, Updated=statement_timestamp() WHERE AD_Tree_ID=10 AND Node_ID=50003 +; + +-- Mar 15, 2018 4:10:13 PM BRT +UPDATE AD_TreeNodeMM SET Parent_ID=50001, SeqNo=4, Updated=statement_timestamp() WHERE AD_Tree_ID=10 AND Node_ID=200153 +; + +-- Mar 15, 2018 4:12:47 PM BRT +INSERT INTO AD_Process_Para (AD_Process_Para_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,Name,Description,AD_Process_ID,SeqNo,AD_Reference_ID,IsRange,FieldLength,IsMandatory,ColumnName,IsCentrallyMaintained,EntityType,AD_Element_ID,AD_Process_Para_UU,IsEncrypted) VALUES (200226,0,0,'Y',TO_TIMESTAMP('2018-03-15 16:12:46','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-03-15 16:12:46','YYYY-MM-DD HH24:MI:SS'),100,'Folder','Path on the server where the pack in files are located',200099,10,10,'N',1000,'Y','Folder','N','D',3012,'e0c4b099-68ba-48fe-b629-043befec7bbe','N') +; + +-- Mar 15, 2018 4:43:24 PM BRT +UPDATE AD_Process SET Help='

This process apply all the pack in files found in a folder recursively - ordered by timestamp.
+The filename of the pack in files is important, it must follow the following convention:
+[Timestamp]_[ClientValue][_AdditionalInformation].zip

+ + + +

NOTE that AD_Client.Value must not contain underscore in order for this process to work.
+There is a special case for ClientValue, if the ClientValue is ALL-CLIENTS then the 2pack is intended to be applied in all active non-system clients.
+If there is a need to apply initially in a seed tenant then the ClientValue must take the form ALL-CLIENTS-Seed.
+For example:
+201803151607_ALL-CLIENTS-GardenWorld_Test123.zip

+',Updated=TO_TIMESTAMP('2018-03-15 16:43:24','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_ID=200099 +; + +SELECT register_migration_script('201803151613_IDEMPIERE-3660.sql') FROM dual +; + diff --git a/org.adempiere.base/src/org/adempiere/base/IDictionaryService.java b/org.adempiere.base/src/org/adempiere/base/IDictionaryService.java index 518634e37d..dded3eac76 100644 --- a/org.adempiere.base/src/org/adempiere/base/IDictionaryService.java +++ b/org.adempiere.base/src/org/adempiere/base/IDictionaryService.java @@ -17,6 +17,7 @@ package org.adempiere.base; import java.io.File; +import org.compiere.model.X_AD_Package_Imp_Proc; import org.osgi.framework.BundleContext; /** @@ -28,4 +29,8 @@ import org.osgi.framework.BundleContext; */ public interface IDictionaryService { void merge(BundleContext context, File packageFile) throws Exception; + + default public X_AD_Package_Imp_Proc getAD_Package_Imp_Proc() { + return null; + }; } diff --git a/org.adempiere.pipo/plugin.xml b/org.adempiere.pipo/plugin.xml index 8152421f9b..219d5dc982 100644 --- a/org.adempiere.pipo/plugin.xml +++ b/org.adempiere.pipo/plugin.xml @@ -35,5 +35,13 @@ class="org.adempiere.pipo2.PackRollProcess"> + + + + 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 0b6c727b1d..31ad2fcf85 100644 --- a/org.adempiere.pipo/src/org/adempiere/pipo/srv/PipoDictionaryService.java +++ b/org.adempiere.pipo/src/org/adempiere/pipo/srv/PipoDictionaryService.java @@ -34,24 +34,28 @@ public class PipoDictionaryService implements IDictionaryService { super(); } + X_AD_Package_Imp_Proc adPackageImp = null; + @Override public void merge(BundleContext context, File packageFile) throws Exception { if (packageFile == null || !packageFile.exists()) { logger.info("No PackIn Model found"); return; } + String symbolicName = "org.adempiere.pipo"; + if (context != null) + symbolicName = context.getBundle().getSymbolicName(); String trxName = null; - X_AD_Package_Imp_Proc adPackageImp = null; PackIn packIn = null; try { trxName = Trx.createTrxName("PipoDS"); Trx.get(trxName, true).setDisplayName(getClass().getName()+"_merge"); packIn = new PackIn(); //external files must not start with "2Pack" prefix in order to work correctly - if ("org.adempiere.pipo".equals(context.getBundle().getSymbolicName()) && !packageFile.getName().startsWith("2Pack")) + if ("org.adempiere.pipo".equals(symbolicName) && !packageFile.getName().startsWith("2Pack")) packIn.setPackageName(packageFile.getName()); else - packIn.setPackageName(context.getBundle().getSymbolicName()); + packIn.setPackageName(symbolicName); if (Env.getCtx().getProperty("#AD_Client_ID") == null) { Env.getCtx().put("#AD_Client_ID", 0); @@ -70,13 +74,13 @@ public class PipoDictionaryService implements IDictionaryService { } } //no version string from file name suffix, get it from bundle header - if (packageVersion == null) + if (packageVersion == null && context != null) packageVersion = (String) context.getBundle().getHeaders().get("Bundle-Version"); packIn.setPackageVersion(packageVersion); packIn.setUpdateDictionary(false); packIn.getNotifier().setFileName(packageFile.getName()); - packIn.getNotifier().setPluginName(context.getBundle().getSymbolicName() + " v" + packageVersion); + packIn.getNotifier().setPluginName(symbolicName + " v" + packageVersion); adPackageImp = new X_AD_Package_Imp_Proc(Env.getCtx(), 0, null); if (logger.isLoggable(Level.INFO)) logger.info("zipFilepath->" + packageFile); @@ -144,4 +148,9 @@ public class PipoDictionaryService implements IDictionaryService { return result; }*/ + @Override + public X_AD_Package_Imp_Proc getAD_Package_Imp_Proc() { + return adPackageImp; + }; + } diff --git a/org.adempiere.pipo/src/org/adempiere/pipo2/PackInFolder.java b/org.adempiere.pipo/src/org/adempiere/pipo2/PackInFolder.java new file mode 100644 index 0000000000..8962b56b1a --- /dev/null +++ b/org.adempiere.pipo/src/org/adempiere/pipo2/PackInFolder.java @@ -0,0 +1,90 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * 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. * + * * + * Contributors: * + * - Carlos Ruiz * + **********************************************************************/ +package org.adempiere.pipo2; + +import java.util.logging.Level; + +import org.adempiere.plugin.utils.PackInApplicationActivator; +import org.compiere.process.ProcessInfoParameter; +import org.compiere.process.SvrProcess; +import org.compiere.util.AdempiereSystemError; +import org.compiere.util.Util; + +/** + * Apply all zip files from a folder following the rules and conventions of Automatic Pack In process + * This process apply all the pack in files found in a folder recursively - ordered by timestamp. + * The filename of the pack in files is important, it must follow the following convention: + * [Timestamp]_[ClientValue][_AdditionalInformation].zip + * - Timestamp: must be in the format yyyymmddHHMM + * - ClientValue: case sensitive and compared against AD_Client.Value to find the tenant where the pack in must be applied + * - AdditionalInformation: Any additional information to identify the zip file + * NOTE that AD_Client.Value must not contain underscore in order for this process to work. + * There is a special case for ClientValue, if the ClientValue is ALL-CLIENTS then the 2pack is intended to be applied in all active non-system clients. + * If there is a need to apply initially in a seed tenant then the ClientValue must take the form ALL-CLIENTS-Seed. + * For example: + * 201803151607_ALL-CLIENTS-GardenWorld_Test123.zip + * + * @author Carlos Ruiz + */ +public class PackInFolder extends SvrProcess { + + /** Folder to apply */ + private String p_Folder; + + /** + * Prepare - e.g., get Parameters. + */ + protected void prepare () { + for (ProcessInfoParameter para : getParameter()) { + String name = para.getParameterName(); + if (para.getParameter() == null) + ; + else if (name.equals("Folder")) + p_Folder = para.getParameterAsString(); + else + log.log(Level.SEVERE, "Unknown Parameter: " + name); + } + } // prepare + + /** + * Process + * @return info + * @throws Exception + */ + protected String doIt () throws Exception + { + if (Util.isEmpty(p_Folder, true)) + throw new AdempiereSystemError("@Mandatory@ @Folder@"); + + PackInApplicationActivator piaa = new PackInApplicationActivator(); + piaa.setProcessInfo(getProcessInfo()); + piaa.setProcessUI(processUI); + piaa.automaticPackin(0, p_Folder, false); + String msg = getProcessInfo().getSummary(); + + return msg; + } // doIt + +} diff --git a/org.adempiere.pipo/src/org/adempiere/pipo2/PackInProcess.java b/org.adempiere.pipo/src/org/adempiere/pipo2/PackInProcess.java index e160aa5c42..8bcfd56027 100644 --- a/org.adempiere.pipo/src/org/adempiere/pipo2/PackInProcess.java +++ b/org.adempiere.pipo/src/org/adempiere/pipo2/PackInProcess.java @@ -45,6 +45,7 @@ public class PackInProcess extends SvrProcess { private String m_packageDirectory = null; public int p_PackIn_ID = 0; + @SuppressWarnings("unused") private String packageName = null; private String packageVersion = null; @@ -130,7 +131,7 @@ public class PackInProcess extends SvrProcess { PackIn packIn = new PackIn(); packIn.setPackageDirectory(m_packageDirectory); - packIn.setPackageName(packageName); + packIn.setPackageName(adPackageImp.getName()); packIn.setPackageVersion(packageVersion); packIn.setUpdateDictionary(m_UpdateDictionary); packIn.getNotifier().setFileName(zipFilepath.getName()); diff --git a/org.adempiere.plugin.utils/META-INF/MANIFEST.MF b/org.adempiere.plugin.utils/META-INF/MANIFEST.MF index 94953eb602..0c6ed763a1 100644 --- a/org.adempiere.plugin.utils/META-INF/MANIFEST.MF +++ b/org.adempiere.plugin.utils/META-INF/MANIFEST.MF @@ -43,6 +43,7 @@ Import-Package: org.adempiere.base, org.compiere, org.compiere.db, org.compiere.model, + org.compiere.process, org.compiere.util, org.osgi.framework;version="1.5.0", org.osgi.util.tracker;version="1.5.0" diff --git a/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/AbstractActivator.java b/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/AbstractActivator.java index f119aecaa7..53f03f4b7a 100644 --- a/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/AbstractActivator.java +++ b/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/AbstractActivator.java @@ -14,13 +14,19 @@ package org.adempiere.plugin.utils; import java.io.File; +import java.util.ArrayList; +import java.util.List; import java.util.logging.Level; import org.adempiere.base.IDictionaryService; +import org.adempiere.base.Service; +import org.adempiere.util.IProcessUI; +import org.compiere.model.MClient; import org.compiere.model.MSysConfig; import org.compiere.model.PO; import org.compiere.model.Query; import org.compiere.model.X_AD_Package_Imp; +import org.compiere.process.ProcessInfo; import org.compiere.util.AdempiereSystemError; import org.compiere.util.CLogger; import org.compiere.util.DB; @@ -38,6 +44,8 @@ public abstract class AbstractActivator implements BundleActivator, ServiceTrack protected ServiceTracker serviceTracker; protected IDictionaryService service; private String trxName = ""; + private ProcessInfo m_processInfo = null; + private IProcessUI m_processUI = null; protected boolean merge(File zipfile, String version) throws Exception { boolean success = false; @@ -46,24 +54,47 @@ public abstract class AbstractActivator implements BundleActivator, ServiceTrack service.merge(context, zipfile); success = true; } else { - logger.log(Level.SEVERE, "The file was already installed: " + zipfile.getName()); + logger.log(Level.SEVERE, "The file was previously installed: " + zipfile.getName()); + } + + return success; + } + + protected boolean directMerge(File zipfile, String version) throws Exception { + boolean success = false; + + if (!installedPackage(version)) { + List list = Service.locator().list(IDictionaryService.class).getServices(); + if (list != null) { + IDictionaryService ids = list.get(0); + ids.merge(null, zipfile); + success = true; + if (ids.getAD_Package_Imp_Proc() != null) { + MClient client = MClient.get(Env.getCtx()); + addLog(Level.INFO, getName() + " in " + client.getValue() + " -> "+ ids.getAD_Package_Imp_Proc().getP_Msg()); + } + } else { + addLog(Level.SEVERE, "Could not find an IDictionaryService to process the zip files"); + } + } else { + addLog(Level.SEVERE, "The file was previously installed: " + zipfile.getName()); + success = true; } return success; } protected boolean installedPackage(String version) { - StringBuilder where = new StringBuilder("Name=? AND PK_Status = 'Completed successfully'"); - Object[] params; + StringBuilder where = new StringBuilder("AD_Client_ID=? AND Name=? AND PK_Status='Completed successfully'"); + List params = new ArrayList(); + params.add(Env.getAD_Client_ID(Env.getCtx())); + params.add(getName()); if (version != null) { where.append(" AND PK_Version LIKE ?"); - params = new Object[] { getName(), version + "%" }; - } else { - params = new Object[] {getName()}; + params.add(version + "%"); } - Query q = new Query(Env.getCtx(), X_AD_Package_Imp.Table_Name, - where.toString(), null); - q.setParameters(params); + Query q = new Query(Env.getCtx(), X_AD_Package_Imp.Table_Name, where.toString(), null) + .setParameters(params); return q.match(); } @@ -105,4 +136,31 @@ public abstract class AbstractActivator implements BundleActivator, ServiceTrack sysconfig.set_TrxName(trxName); return sysconfig; } + + public void setProcessInfo(ProcessInfo processInfo) { + m_processInfo = processInfo; + } + + public void setProcessUI(IProcessUI processUI) { + m_processUI = processUI; + }; + + protected void statusUpdate(String message) { + logger.warning(message); + if (m_processUI != null) + m_processUI.statusUpdate(message); + } + + public void addLog(Level level, String msg) { + logger.log(level, msg); + if (m_processInfo != null) + m_processInfo.addLog(0, null, null, msg.replaceAll("\\n", "
")); + } + + public void setSummary(Level level, String msg) { + logger.log(level, msg); + if (m_processInfo != null) + m_processInfo.setSummary(msg); + } + } diff --git a/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/PackInApplicationActivator.java b/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/PackInApplicationActivator.java index 44764cd4dc..5158cd9814 100644 --- a/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/PackInApplicationActivator.java +++ b/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/PackInApplicationActivator.java @@ -15,6 +15,7 @@ package org.adempiere.plugin.utils; import java.io.File; import java.io.FileFilter; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -34,6 +35,8 @@ import org.compiere.model.MSysConfig; import org.compiere.model.Query; import org.compiere.model.ServerStateChangeEvent; import org.compiere.model.ServerStateChangeListener; +import org.compiere.model.X_AD_Package_Imp; +import org.compiere.model.X_AD_Package_Imp_Proc; import org.compiere.util.AdempiereSystemError; import org.compiere.util.CLogger; import org.compiere.util.Env; @@ -88,7 +91,9 @@ public class PackInApplicationActivator extends AbstractActivator { Adempiere.getThreadPoolExecutor().execute(new Runnable() { @Override public void run() { - automaticPackin(); + int timeout = MSysConfig.getIntValue(MSysConfig.AUTOMATIC_PACKIN_INITIAL_DELAY, 120) * 1000; + String folders = MSysConfig.getValue(MSysConfig.AUTOMATIC_PACKIN_FOLDERS); + automaticPackin(timeout, folders, true); } }); } else { @@ -96,7 +101,9 @@ public class PackInApplicationActivator extends AbstractActivator { @Override public void stateChange(ServerStateChangeEvent event) { if (event.getEventType() == ServerStateChangeEvent.SERVER_START && service != null) { - automaticPackin(); + int timeout = MSysConfig.getIntValue(MSysConfig.AUTOMATIC_PACKIN_INITIAL_DELAY, 120) * 1000; + String folders = MSysConfig.getValue(MSysConfig.AUTOMATIC_PACKIN_FOLDERS); + automaticPackin(timeout, folders, true); } } }); @@ -104,73 +111,90 @@ public class PackInApplicationActivator extends AbstractActivator { return null; } - private void automaticPackin() { - //Initial delay - Timer t = new Timer(); - t.schedule(new TimerTask() { - @Override - public void run() { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - try { - Thread.currentThread().setContextClassLoader(PackInApplicationActivator.class.getClassLoader()); - setupPackInContext(); - installPackages(); - } finally { - ServerContext.dispose(); - service = null; - Thread.currentThread().setContextClassLoader(cl); + public void automaticPackin(int timeout, String folders, boolean fromService) { + if (fromService) { + //Initial delay - starting from service + Timer t = new Timer(); + t.schedule(new TimerTask() { + @Override + public void run() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(PackInApplicationActivator.class.getClassLoader()); + setupPackInContext(); + installPackages(folders); + } finally { + ServerContext.dispose(); + service = null; + Thread.currentThread().setContextClassLoader(cl); + } + t.cancel(); } - t.cancel(); + }, timeout); + } else { + // No delay - starting from process + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(PackInApplicationActivator.class.getClassLoader()); + setupPackInContext(); + installPackages(folders); + } finally { + ServerContext.dispose(); + Thread.currentThread().setContextClassLoader(cl); } - }, MSysConfig.getIntValue(MSysConfig.AUTOMATIC_PACKIN_INITIAL_DELAY, 120) * 1000); + } } - private void installPackages() { - - String folders = MSysConfig.getValue(MSysConfig.AUTOMATIC_PACKIN_FOLDERS); - + private void installPackages(String folders) { if (Util.isEmpty(folders, true)) { - logger.log(Level.INFO, "Not specified folders for automatic packin"); + setSummary(Level.INFO, "Not specified folders for automatic packin"); return; } File[] fileArray = getFilesToProcess(folders); if (fileArray.length <= 0) { - logger.info("No zip files to process"); + setSummary(Level.INFO, "No zip files to process"); return; } try { if (getDBLock()) { //Create Session to be able to create records in AD_ChangeLog - MSession.get(Env.getCtx(), true); + if (Env.getContextAsInt(Env.getCtx(), "#AD_Session_ID") <= 0) + MSession.get(Env.getCtx(), true); for(File zipFile : fileArray) { currentFile = zipFile; if (!packIn(zipFile)) { // stop processing further packages if one fail + addLog(Level.SEVERE, "Failed application of " + zipFile); break; } + addLog(Level.INFO, "Successful application of " + zipFile); filesToProcess.remove(zipFile); } - releaseLock(); } else { - logger.log(Level.SEVERE, "Could not acquire the DB lock to automatically install the packins"); + addLog(Level.SEVERE, "Could not acquire the DB lock to automatically install the packins"); + return; } } catch (AdempiereSystemError e) { e.printStackTrace(); + addLog(Level.SEVERE, e.getLocalizedMessage()); + } finally { + releaseLock(); } if (filesToProcess.size() > 0) { - logger.warning("The following packages were not applied: "); + StringBuilder pending = new StringBuilder("The following packages were not applied: "); for (File file : filesToProcess) { - logger.warning(file.getName()); + pending.append("\n").append(file.getName()); } + addLog(Level.WARNING, pending.toString()); } } private boolean packIn(File packinFile) { - if (packinFile != null && service != null) { + if (packinFile != null) { String fileName = packinFile.getName(); logger.warning("Installing " + fileName + " ..."); @@ -178,23 +202,86 @@ public class PackInApplicationActivator extends AbstractActivator { String [] parts = fileName.split("_"); String clientValue = parts[1]; - MClient client = getClient(clientValue); - if (client == null) { - logger.log(Level.SEVERE, "Client does not exist: " + clientValue); - return false; + boolean allClients = clientValue.startsWith("ALL-CLIENTS"); + + int[] clientIDs; + if (allClients) { + int[] seedClientIDs = new int[0]; + String seedClientValue = ""; + if (clientValue.startsWith("ALL-CLIENTS-")) { + seedClientValue = clientValue.split("-")[2]; + seedClientIDs = getClientIDs(seedClientValue); + if (seedClientIDs.length == 0) { + logger.log(Level.SEVERE, "Seed client does not exist: " + seedClientValue); + return false; + } + } + int[] allClientIDs = new Query(Env.getCtx(), MClient.Table_Name, "AD_Client_ID>0 AND Value!=?", null) + .setOnlyActiveRecords(true) + .setParameters(seedClientValue) + .setOrderBy("AD_Client_ID") + .getIDs(); + // Process first the seed client, put seed in front of the array + int shift = 0; + if (seedClientIDs.length > 0) + shift = 1; + clientIDs = new int[allClientIDs.length + shift]; + if (seedClientIDs.length > 0) + clientIDs[0] = seedClientIDs[0]; + for (int i = 0; i < allClientIDs.length; i++) { + clientIDs[i+shift] = allClientIDs[i]; + } + } else { + clientIDs = getClientIDs(clientValue); + if (clientIDs.length == 0) { + logger.log(Level.SEVERE, "Client does not exist: " + clientValue); + return false; + } } - Env.setContext(Env.getCtx(), "#AD_Client_ID", client.getAD_Client_ID()); - - try { - // call 2pack - if (!merge(packinFile, null)) + for (int clientID : clientIDs) { + MClient client = MClient.get(Env.getCtx(), clientID); + if (allClients) { + String message = "Installing " + fileName + " in client " + client.getValue() + "/" + client.getName(); + statusUpdate(message); + } + Env.setContext(Env.getCtx(), "#AD_Client_ID", client.getAD_Client_ID()); + try { + // call 2pack + if (service != null) { + if (!merge(packinFile, null)) { + return false; + } + } else { + if (!directMerge(packinFile, null)) { + return false; + } + } + } catch (Throwable e) { + logger.log(Level.SEVERE, "Pack in failed.", e); return false; - } catch (Throwable e) { - logger.log(Level.SEVERE, "Pack in failed.", e); - return false; + } finally { + Env.setContext(Env.getCtx(), "#AD_Client_ID", 0); + } + logger.warning(packinFile.getPath() + " installed"); + } + if (allClients ) { + // when arriving here it means an ALL-CLIENTS 2pack was processed successfully + // register a record on System to avoid future reprocesses of the same file + X_AD_Package_Imp_Proc pimpr = new X_AD_Package_Imp_Proc(Env.getCtx(), 0, null); + pimpr.setName(fileName); + pimpr.setDateProcessed(new Timestamp(System.currentTimeMillis())); + pimpr.setP_Msg("This ALL-CLIENT 2Pack was applied successfully in all tenants"); + pimpr.setAD_Package_Source_Type(X_AD_Package_Imp_Proc.AD_PACKAGE_SOURCE_TYPE_File); + pimpr.saveEx(); + X_AD_Package_Imp pimp = new X_AD_Package_Imp(Env.getCtx(), 0, null); + pimp.setAD_Package_Imp_Proc_ID(pimpr.getAD_Package_Imp_Proc_ID()); + pimp.setName(fileName); + pimp.setPK_Status("Completed successfully"); + pimp.setDescription("This ALL-CLIENT 2Pack was applied successfully in all tenants"); + pimp.setProcessed(true); + pimp.saveEx(); } - logger.warning(packinFile.getPath() + " installed"); } return true; @@ -282,14 +369,12 @@ public class PackInApplicationActivator extends AbstractActivator { } } - private MClient getClient(String clientValue) { + private int[] getClientIDs(String clientValue) { String where = "Value = ?"; - Query q = new Query(Env.getCtx(), MClient.Table_Name, - where, null) - .setParameters(new Object[] {clientValue}) + Query q = new Query(Env.getCtx(), MClient.Table_Name, where, null) + .setParameters(clientValue) .setOnlyActiveRecords(true); - - return q.first(); + return q.getIDs(); } @Override @@ -312,6 +397,6 @@ public class PackInApplicationActivator extends AbstractActivator { Properties serverContext = new Properties(); serverContext.setProperty("#AD_Client_ID", "0"); ServerContext.setCurrentInstance(serverContext); - }; + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessDialog.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessDialog.java index c92169b069..a46511e738 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessDialog.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessDialog.java @@ -314,7 +314,7 @@ public class ProcessDialog extends AbstractProcessDialog implements EventListene layoutResultPanel (topParameterLayout); StringBuilder buildMsg = new StringBuilder(getInitialMessage()); - buildMsg.append("

** ") + buildMsg.append("


** ") .append(pi.getSummary()) .append("

"); diff --git a/org.adempiere.ui.zk/theme/default/css/fragment/parameter-process.css.dsp b/org.adempiere.ui.zk/theme/default/css/fragment/parameter-process.css.dsp index 022b789362..9e17d1df13 100644 --- a/org.adempiere.ui.zk/theme/default/css/fragment/parameter-process.css.dsp +++ b/org.adempiere.ui.zk/theme/default/css/fragment/parameter-process.css.dsp @@ -36,7 +36,7 @@ when detect side effect, fix to only apply for parameter window*/ } .message-paramenter{ - max-height: 150pt; + max-height: 300pt; overflow: hidden; margin: 10px; }