/*********************************************************************** * 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. * * * * Sponsor: * * - FH * * Contributors: * * - Carlos Ruiz * **********************************************************************/ package org.idempiere.process; import java.sql.SQLException; import java.sql.Timestamp; import java.util.logging.Level; import org.compiere.model.IArchiveStore; import org.compiere.model.IAttachmentStore; import org.compiere.model.IImageStore; import org.compiere.model.MArchive; import org.compiere.model.MAttachment; import org.compiere.model.MAttachmentEntry; import org.compiere.model.MClient; import org.compiere.model.MClientInfo; import org.compiere.model.MImage; import org.compiere.model.MProcessPara; import org.compiere.model.MStorageProvider; import org.compiere.model.Query; import org.compiere.process.ProcessInfoParameter; import org.compiere.process.SvrProcess; import org.compiere.util.CacheMgt; /** * IDEMPIERE-4191 * @author Carlos Ruiz - globalqss */ @org.adempiere.base.annotation.Process public class MigrateStorageProvider extends SvrProcess { private int p_AD_Client_ID = -1; private int p_Actual_StorageProvider_ID = 0; private int p_AD_StorageProvider_ID = -1; private boolean p_IsMigrateAttachment = false; private boolean p_IsMigrateArchive = false; private boolean p_IsMigrateImage = false; private boolean p_IsMigrateData = true; private int p_IDFrom = 0; private int p_IDTo = 0; private boolean p_DeleteOld = false; int cntAttachment = 0; int cntArchive = 0; int cntImage = 0; /** * Prepare - e.g., get Parameters. */ protected void prepare() { for (ProcessInfoParameter para : getParameter()) { String name = para.getParameterName(); if ("AD_AllClients_V_ID".equals(name)) { if (para.getParameter() != null) { p_AD_Client_ID = para.getParameterAsInt(); } } else if ("Actual_StorageProvider_ID".equals(name)) { p_Actual_StorageProvider_ID = para.getParameterAsInt(); } else if ("AD_StorageProvider_ID".equals(name)) { p_AD_StorageProvider_ID = para.getParameterAsInt(); } else if ("IsMigrateAttachment".equals(name)) { p_IsMigrateAttachment = para.getParameterAsBoolean(); } else if ("IsMigrateArchive".equals(name)) { p_IsMigrateArchive = para.getParameterAsBoolean(); } else if ("IsMigrateImage".equals(name)) { p_IsMigrateImage = para.getParameterAsBoolean(); } else if ("IsMigrateData".equals(name)) { p_IsMigrateData = para.getParameterAsBoolean(); } else if ("Record_ID".equals(name)) { p_IDFrom = para.getParameterAsInt(); p_IDTo = para.getParameter_ToAsInt(); } else if ("DeleteOld".equals(name)) { p_DeleteOld = para.getParameterAsBoolean(); } else { MProcessPara.validateUnknownParameter(getProcessInfo().getAD_Process_ID(), para); } } } // prepare /** * Perform process. * @return Message * @throws Exception */ protected String doIt() throws Exception { if (log.isLoggable(Level.INFO)) log.info("AD_AllClients_V_ID" + p_AD_Client_ID + ", Actual_StorageProvider_ID=" + p_Actual_StorageProvider_ID + ", AD_StorageProvider_ID=" + p_AD_StorageProvider_ID + ", IsMigrateAttachment=" + p_IsMigrateAttachment + ", IsMigrateArchive=" + p_IsMigrateArchive + ", IsMigrateImage=" + p_IsMigrateImage + ", IsMigrateData=" + p_IsMigrateData + ", IDFrom=" + p_IDFrom + ", IDTo=" + p_IDTo + ", DeleteOld=" + p_DeleteOld); if ( ! (p_IsMigrateAttachment || p_IsMigrateArchive || p_IsMigrateImage ) ) { return "Nothing to migrate, please select an option"; } MStorageProvider newProvider = MStorageProvider.get(getCtx(), p_AD_StorageProvider_ID); // Create list of clients to process: // - single AD_Client // - clients using the actual storage provider (depending on the isMigrate flags) int[] clientsToUpdate; if (p_AD_Client_ID >= 0) { clientsToUpdate = new int[] {p_AD_Client_ID}; p_Actual_StorageProvider_ID = 0; } else { StringBuilder whereClause = new StringBuilder(); StringBuilder storageClause = new StringBuilder(); if (p_Actual_StorageProvider_ID > 0) { storageClause.append("=").append(p_Actual_StorageProvider_ID); } else { storageClause.append(" IS NULL"); } if (p_IsMigrateAttachment) { whereClause.append("AD_StorageProvider_ID").append(storageClause); } if (p_IsMigrateArchive) { if (whereClause.length() > 0) { whereClause.append(" OR "); } whereClause.append("StorageArchive_ID").append(storageClause); } if (p_IsMigrateImage) { if (whereClause.length() > 0) { whereClause.append(" OR "); } whereClause.append("StorageImage_ID").append(storageClause); } clientsToUpdate = new Query(getCtx(), MClientInfo.Table_Name, whereClause.toString(), get_TrxName()) .setOrderBy("AD_Client_ID") .getIDs(); } // Start the process updating the provider, data migration goes later for (int clientid : clientsToUpdate) { MClientInfo clientInfo = MClientInfo.getCopy(getCtx(), clientid, (String)null); MClient client = MClient.get(getCtx(), clientid); if ( p_IsMigrateAttachment && clientInfo.getAD_StorageProvider_ID() != p_AD_StorageProvider_ID) { clientInfo.setAD_StorageProvider_ID(p_AD_StorageProvider_ID); clientInfo.saveEx(); String msg = "Attachment provider set to " + newProvider.getName() + " on " + client.getName(); addLog(msg); } if ( p_IsMigrateArchive && clientInfo.getStorageArchive_ID() != p_AD_StorageProvider_ID) { clientInfo.setStorageArchive_ID(p_AD_StorageProvider_ID); clientInfo.saveEx(); String msg = "Archive provider set to " + newProvider.getName() + " on " + client.getName(); addLog(msg); } if ( p_IsMigrateImage && clientInfo.getStorageImage_ID() != p_AD_StorageProvider_ID) { clientInfo.setStorageImage_ID(p_AD_StorageProvider_ID); clientInfo.saveEx(); String msg = "Image provider set to " + newProvider.getName() + " on " + client.getName(); addLog(msg); } } commitEx(); CacheMgt.get().reset(MClientInfo.Table_Name); if (p_IsMigrateData) { int[] clientsToMigrate; if (p_AD_Client_ID >= 0) { clientsToMigrate = new int[] {p_AD_Client_ID}; } else { final String whereMigrate = "(AD_StorageProvider_ID=? OR StorageArchive_ID=? OR StorageImage_ID=?)"; clientsToMigrate = new Query(getCtx(), MClientInfo.Table_Name, whereMigrate, get_TrxName()) .setOrderBy("AD_Client_ID") .setParameters(p_AD_StorageProvider_ID, p_AD_StorageProvider_ID, p_AD_StorageProvider_ID) .getIDs(); } int idxClient = 0; int totalClients = clientsToMigrate.length; // for each client for (int clientid : clientsToMigrate) { idxClient++; MClient client = MClient.get(getCtx(), clientid); int odometer = 10; if (p_IsMigrateAttachment) { migrateAttachments(newProvider, idxClient, totalClients, clientid, client, odometer); } if (p_IsMigrateArchive) { migrateArchives(newProvider, idxClient, totalClients, clientid, client, odometer); } if (p_IsMigrateImage) { migrateImages(newProvider, idxClient, totalClients, clientid, client, odometer); } } // end for each client } return "@Updated@ " + cntAttachment + " @AD_Attachment_ID@, " + cntArchive + " @AD_Archive_ID@, " + cntImage + " @AD_Image_ID@"; } // doIt private void migrateAttachments(MStorageProvider newProvider, int idxClient, int totalClients, int clientid, MClient client, int odometer) throws SQLException { // migrate attachment status(idxClient, totalClients, "Migrating attachments for " + client.getName()); final String where = "AD_Client_ID=? AND (?=0 OR AD_Attachment_ID>=?) AND (?=0 OR AD_Attachment_ID<=?) AND COALESCE(AD_StorageProvider_ID,0)!=?"; int[] attachIds = new Query(getCtx(), MAttachment.Table_Name, where, get_TrxName()) .setParameters(clientid, p_IDFrom, p_IDFrom, p_IDTo, p_IDTo, p_AD_StorageProvider_ID) .setOrderBy("AD_Attachment_ID") .getIDs(); int cntRecords = attachIds.length; // iterate on each record of the associated table int idxAttach = 0; for (int attachId : attachIds) { idxAttach++; if (idxAttach % odometer == 0) { progress(idxClient, totalClients, idxAttach, cntRecords, "Migrating attachment "); } MAttachment attachment = new MAttachment(getCtx(), attachId, get_TrxName()); int oldProviderId = attachment.getAD_StorageProvider_ID(); for (MAttachmentEntry entry : attachment.getEntries()) { entry.getData(); // force load in case old provider is delayed } attachment.setStorageProvider(newProvider); attachment.set_ValueNoCheck("Updated", new Timestamp(System.currentTimeMillis())); // to force save // create file on the new storage provider attachment.saveEx(); cntAttachment++; // commit on every record migrated commitEx(); if (p_DeleteOld) { MStorageProvider oldProvider = MStorageProvider.get(getCtx(), oldProviderId); if (! (oldProviderId == 0 || MStorageProvider.METHOD_Database.equals(oldProvider.getMethod()))) { // DB method doesn't require delete IAttachmentStore oldStore = oldProvider.getAttachmentStore(); // delete file on old storage oldStore.delete(attachment, oldProvider); commitEx(); } } } String msg = "Migrated " + cntRecords + " attachments on " + client.getName(); addLog(msg); } private void migrateArchives(MStorageProvider newProvider, int idxClient, int totalClients, int clientid, MClient client, int odometer) throws SQLException { // migrate archive status(idxClient, totalClients, "Migrating archives for " + client.getName()); final String where = "AD_Client_ID=? AND (?=0 OR AD_Archive_ID>=?) AND (?=0 OR AD_Archive_ID<=?) AND COALESCE(AD_StorageProvider_ID,0)!=?"; int[] archiveIds = new Query(getCtx(), MArchive.Table_Name, where, get_TrxName()) .setParameters(clientid, p_IDFrom, p_IDFrom, p_IDTo, p_IDTo, p_AD_StorageProvider_ID) .setOrderBy("AD_Archive_ID") .getIDs(); int cntRecords = archiveIds.length; // iterate on each record of the associated table int idxArchive = 0; for (int archiveId : archiveIds) { idxArchive++; if (idxArchive % odometer == 0) { progress(idxClient, totalClients, idxArchive, cntRecords, "Migrating archive "); } MArchive archive = new MArchive(getCtx(), archiveId, get_TrxName()); int oldProviderId = archive.getAD_StorageProvider_ID(); byte[] data = archive.getBinaryData(); archive.setStorageProvider(newProvider); archive.setBinaryData(data); archive.set_ValueNoCheck("Updated", new Timestamp(System.currentTimeMillis())); // to force save // create file on the new storage provider archive.saveEx(); cntArchive++; // commit on every record migrated commitEx(); if (p_DeleteOld) { MStorageProvider oldProvider = MStorageProvider.get(getCtx(), oldProviderId); if (! (oldProviderId == 0 || MStorageProvider.METHOD_Database.equals(oldProvider.getMethod()))) { // DB method doesn't require delete IArchiveStore oldStore = oldProvider.getArchiveStore(); // delete file on old storage oldStore.deleteArchive(archive, oldProvider); commitEx(); } } } String msg = "Migrated " + cntRecords + " archives on " + client.getName(); addLog(msg); } private void migrateImages(MStorageProvider newProvider, int idxClient, int totalClients, int clientid, MClient client, int odometer) throws SQLException { // migrate image status(idxClient, totalClients, "Migrating images for " + client.getName()); final String where = "AD_Client_ID=? AND (?=0 OR AD_Image_ID>=?) AND (?=0 OR AD_Image_ID<=?) AND COALESCE(AD_StorageProvider_ID,0)!=?"; int[] imageIds = new Query(getCtx(), MImage.Table_Name, where, get_TrxName()) .setParameters(clientid, p_IDFrom, p_IDFrom, p_IDTo, p_IDTo, p_AD_StorageProvider_ID) .setOrderBy("AD_Image_ID") .getIDs(); int cntRecords = imageIds.length; // iterate on each record of the associated table int idxImage = 0; for (int imageId : imageIds) { idxImage++; if (idxImage % odometer == 0) { progress(idxClient, totalClients, idxImage, cntRecords, "Migrating image "); } MImage image = new MImage(getCtx(), imageId, get_TrxName()); int oldProviderId = image.getAD_StorageProvider_ID(); byte[] data = image.getBinaryData(); image.setStorageProvider(newProvider); image.setBinaryData(data); image.set_ValueNoCheck("Updated", new Timestamp(System.currentTimeMillis())); // to force save // create file on the new storage provider image.saveEx(); // commit on every record migrated commitEx(); if (p_DeleteOld) { MStorageProvider oldProvider = MStorageProvider.get(getCtx(), oldProviderId); if (! (oldProviderId == 0 || MStorageProvider.METHOD_Database.equals(oldProvider.getMethod()))) { // DB method doesn't require delete IImageStore oldStore = oldProvider.getImageStore(); // delete file on old storage oldStore.delete(image, oldProvider); commitEx(); } } } String msg = "Migrated " + cntRecords + " images on " + client.getName(); addLog(msg); } private void progress(int idxClient, int totalClients, int idxRecord, int cntRecords, String msg) { if (processUI != null) { processUI.statusUpdate("Tenant " + idxClient + "/" + totalClients + " - " + msg + " " + idxRecord + "/" + cntRecords + " = " + idxRecord*100/cntRecords + "%"); } } private void status(int idxClient, int totalClients, String msg) { if (processUI != null) { processUI.statusUpdate("Tenant " + idxClient + "/" + totalClients + " - " + msg); } } } // MigrateStorageProvider