IDEMPIERE-4190 Implement Image Storage Provider (1006528) (FHCA-1165)

integrate development from Heng Sin
This commit is contained in:
Carlos Ruiz 2020-02-27 21:37:32 +01:00
parent ea1f86837d
commit 55356515b5
17 changed files with 727 additions and 22 deletions

View File

@ -0,0 +1,43 @@
SET SQLBLANKLINES ON
SET DEFINE OFF
-- Aug 18, 2016 9:03:49 PM MYT
-- 1006528 Implement Image Storage Provider
INSERT INTO AD_Element (AD_Element_ID,ColumnName,Updated,Name,Description,PrintName,AD_Element_UU,IsActive,Created,AD_Org_ID,CreatedBy,UpdatedBy,AD_Client_ID,EntityType) VALUES (203038,'StorageImage_ID',TO_DATE('2016-08-18 21:03:42','YYYY-MM-DD HH24:MI:SS'),'Image Store','Storage provider for Image','Image Store','a4df3881-4d7e-4b3b-a71a-3380bdebf371','Y',TO_DATE('2016-08-18 21:03:42','YYYY-MM-DD HH24:MI:SS'),0,100,100,0,'D')
;
-- Aug 18, 2016 9:05:23 PM MYT
INSERT INTO AD_Column (AD_Column_ID,SeqNoSelection,IsSyncDatabase,Version,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,Updated,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,IsActive,CreatedBy,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,AD_Client_ID,AD_Org_ID,Created,EntityType,IsEncrypted,IsSecure,FKConstraintType,AD_Element_ID,AD_Reference_ID,AD_Reference_Value_ID,AD_Table_ID) VALUES (212831,0,'N',0,'N','N','N',0,'N',22,'N','N','N','Y','a3d7a4ea-9d2e-4da6-b48f-7ee5242719ee',TO_DATE('2016-08-18 21:05:07','YYYY-MM-DD HH24:MI:SS'),'Y','StorageImage_ID','Storage provider for Image','Image Store','Y','Y',100,100,'N','N',0,0,TO_DATE('2016-08-18 21:05:07','YYYY-MM-DD HH24:MI:SS'),'D','N','N','N',203038,18,200023,227)
;
-- Aug 18, 2016 9:05:59 PM MYT
UPDATE AD_Column SET FKConstraintType='N', FKConstraintName='StorageImage_ADClientInfo',Updated=TO_DATE('2016-08-18 21:05:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=212831
;
-- Aug 18, 2016 9:05:59 PM MYT
ALTER TABLE AD_ClientInfo ADD StorageImage_ID NUMBER(10) DEFAULT NULL
;
-- Aug 18, 2016 9:06:00 PM MYT
ALTER TABLE AD_ClientInfo ADD CONSTRAINT StorageImage_ADClientInfo FOREIGN KEY (StorageImage_ID) REFERENCES ad_storageprovider(ad_storageprovider_id) DEFERRABLE INITIALLY DEFERRED
;
-- Aug 18, 2016 9:07:51 PM MYT
INSERT INTO AD_Field (SortNo,AD_Field_ID,IsEncrypted,DisplayLength,IsSameLine,IsHeading,SeqNo,IsCentrallyMaintained,IsReadOnly,AD_Org_ID,Updated,Description,Name,AD_Field_UU,IsDisplayed,IsFieldOnly,CreatedBy,UpdatedBy,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,Created,ColumnSpan,NumLines,IsAdvancedField,IsDefaultFocus,AD_Column_ID,EntityType,AD_Tab_ID) VALUES (0,204272,'N',0,'N','N',1020,'Y','N',0,TO_DATE('2016-08-18 21:07:45','YYYY-MM-DD HH24:MI:SS'),'Storage provider for Image','Image Store','471cc623-0460-4b6a-8e24-74055858ce8a','Y','N',100,100,'Y','Y',1020,1,'N',0,TO_DATE('2016-08-18 21:07:45','YYYY-MM-DD HH24:MI:SS'),2,1,'N','N',212831,'D',169)
;
-- Aug 18, 2016 9:08:51 PM MYT
UPDATE AD_Field SET SeqNo=120, IsDisplayed='Y', XPosition=1,Updated=TO_DATE('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204272
;
-- Aug 18, 2016 9:08:51 PM MYT
UPDATE AD_Field SET SeqNo=125,Updated=TO_DATE('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202532
;
-- Aug 18, 2016 9:08:51 PM MYT
UPDATE AD_Field SET SeqNo=130,Updated=TO_DATE('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202533
;
SELECT register_migration_script('201608191500_Ticket_1006528.sql') FROM dual
;

View File

@ -0,0 +1,40 @@
-- Aug 18, 2016 9:03:49 PM MYT
-- 1006528 Implement Image Storage Provider
INSERT INTO AD_Element (AD_Element_ID,ColumnName,Updated,Name,Description,PrintName,AD_Element_UU,IsActive,Created,AD_Org_ID,CreatedBy,UpdatedBy,AD_Client_ID,EntityType) VALUES (203038,'StorageImage_ID',TO_TIMESTAMP('2016-08-18 21:03:42','YYYY-MM-DD HH24:MI:SS'),'Image Store','Storage provider for Image','Image Store','a4df3881-4d7e-4b3b-a71a-3380bdebf371','Y',TO_TIMESTAMP('2016-08-18 21:03:42','YYYY-MM-DD HH24:MI:SS'),0,100,100,0,'D')
;
-- Aug 18, 2016 9:05:23 PM MYT
INSERT INTO AD_Column (AD_Column_ID,SeqNoSelection,IsSyncDatabase,Version,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,Updated,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,IsActive,CreatedBy,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,AD_Client_ID,AD_Org_ID,Created,EntityType,IsEncrypted,IsSecure,FKConstraintType,AD_Element_ID,AD_Reference_ID,AD_Reference_Value_ID,AD_Table_ID) VALUES (212831,0,'N',0,'N','N','N',0,'N',22,'N','N','N','Y','a3d7a4ea-9d2e-4da6-b48f-7ee5242719ee',TO_TIMESTAMP('2016-08-18 21:05:07','YYYY-MM-DD HH24:MI:SS'),'Y','StorageImage_ID','Storage provider for Image','Image Store','Y','Y',100,100,'N','N',0,0,TO_TIMESTAMP('2016-08-18 21:05:07','YYYY-MM-DD HH24:MI:SS'),'D','N','N','N',203038,18,200023,227)
;
-- Aug 18, 2016 9:05:59 PM MYT
UPDATE AD_Column SET FKConstraintType='N', FKConstraintName='StorageImage_ADClientInfo',Updated=TO_TIMESTAMP('2016-08-18 21:05:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=212831
;
-- Aug 18, 2016 9:05:59 PM MYT
ALTER TABLE AD_ClientInfo ADD COLUMN StorageImage_ID NUMERIC(10) DEFAULT NULL
;
-- Aug 18, 2016 9:06:00 PM MYT
ALTER TABLE AD_ClientInfo ADD CONSTRAINT StorageImage_ADClientInfo FOREIGN KEY (StorageImage_ID) REFERENCES ad_storageprovider(ad_storageprovider_id) DEFERRABLE INITIALLY DEFERRED
;
-- Aug 18, 2016 9:07:51 PM MYT
INSERT INTO AD_Field (SortNo,AD_Field_ID,IsEncrypted,DisplayLength,IsSameLine,IsHeading,SeqNo,IsCentrallyMaintained,IsReadOnly,AD_Org_ID,Updated,Description,Name,AD_Field_UU,IsDisplayed,IsFieldOnly,CreatedBy,UpdatedBy,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,Created,ColumnSpan,NumLines,IsAdvancedField,IsDefaultFocus,AD_Column_ID,EntityType,AD_Tab_ID) VALUES (0,204272,'N',0,'N','N',1020,'Y','N',0,TO_TIMESTAMP('2016-08-18 21:07:45','YYYY-MM-DD HH24:MI:SS'),'Storage provider for Image','Image Store','471cc623-0460-4b6a-8e24-74055858ce8a','Y','N',100,100,'Y','Y',1020,1,'N',0,TO_TIMESTAMP('2016-08-18 21:07:45','YYYY-MM-DD HH24:MI:SS'),2,1,'N','N',212831,'D',169)
;
-- Aug 18, 2016 9:08:51 PM MYT
UPDATE AD_Field SET SeqNo=120, IsDisplayed='Y', XPosition=1,Updated=TO_TIMESTAMP('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204272
;
-- Aug 18, 2016 9:08:51 PM MYT
UPDATE AD_Field SET SeqNo=125,Updated=TO_TIMESTAMP('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202532
;
-- Aug 18, 2016 9:08:51 PM MYT
UPDATE AD_Field SET SeqNo=130,Updated=TO_TIMESTAMP('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202533
;
SELECT register_migration_script('201608191500_Ticket_1006528.sql') FROM dual
;

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.compiere.model.ImageDBStorageImpl">
<implementation class="org.compiere.model.ImageDBStorageImpl"/>
<service>
<provide interface="org.compiere.model.IImageStore"/>
</service>
<property name="method" type="String" value="DB"/>
</scr:component>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.compiere.model.ImageFileStorageImpl">
<implementation class="org.compiere.model.ImageFileStorageImpl"/>
<service>
<provide interface="org.compiere.model.IImageStore"/>
</service>
<property name="method" type="String" value="FileSystem"/>
</scr:component>

View File

@ -113,4 +113,13 @@ public class ArchiveDB implements IArchiveStore {
return true; return true;
} }
@Override
public boolean isPendingFlush() {
return false;
}
@Override
public void flush(MArchive archive, MStorageProvider prov) {
}
} }

View File

@ -53,6 +53,8 @@ public class ArchiveFileSystem implements IArchiveStore {
private static final CLogger log = CLogger.getCLogger(ArchiveFileSystem.class); private static final CLogger log = CLogger.getCLogger(ArchiveFileSystem.class);
//temporary buffer when AD_Archive_ID=0;
private byte[] buffer;
/* (non-Javadoc) /* (non-Javadoc)
* @see org.compiere.model.IArchiveStore#loadLOBData(org.compiere.model.MArchive, org.compiere.model.MStorageProvider) * @see org.compiere.model.IArchiveStore#loadLOBData(org.compiere.model.MArchive, org.compiere.model.MStorageProvider)
@ -63,6 +65,7 @@ public class ArchiveFileSystem implements IArchiveStore {
if ("".equals(archivePathRoot)) { if ("".equals(archivePathRoot)) {
throw new IllegalArgumentException("no attachmentPath defined"); throw new IllegalArgumentException("no attachmentPath defined");
} }
buffer = null;
byte[] data = archive.getByteData(); byte[] data = archive.getByteData();
if (data == null) { if (data == null) {
return null; return null;
@ -144,24 +147,29 @@ public class ArchiveFileSystem implements IArchiveStore {
* @see org.compiere.model.IArchiveStore#save(org.compiere.model.MArchive, org.compiere.model.MStorageProvider) * @see org.compiere.model.IArchiveStore#save(org.compiere.model.MArchive, org.compiere.model.MStorageProvider)
*/ */
@Override @Override
public void save(MArchive archive, MStorageProvider prov,byte[] inflatedData) { public void save(MArchive archive, MStorageProvider prov,byte[] inflatedData) {
String archivePathRoot = getArchivePathRoot(prov);
if ("".equals(archivePathRoot)) {
throw new IllegalArgumentException("no attachmentPath defined");
}
if (inflatedData == null || inflatedData.length == 0) { if (inflatedData == null || inflatedData.length == 0) {
throw new IllegalArgumentException("InflatedData is NULL"); throw new IllegalArgumentException("InflatedData is NULL");
} }
if(archive.get_ID()==0){ if(archive.get_ID()==0){
//set binary data otherwise save will fail //set binary data otherwise save will fail
archive.setByteData(new byte[]{'0'}); archive.setByteData(new byte[]{'0'});
if(!archive.save()) { buffer = inflatedData;
throw new IllegalArgumentException("unable to save MArchive"); } else {
} write(archive, prov, inflatedData);
} }
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); }
private void write(MArchive archive, MStorageProvider prov,
byte[] inflatedData) {
BufferedOutputStream out = null; BufferedOutputStream out = null;
try { try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
String archivePathRoot = getArchivePathRoot(prov);
if ("".equals(archivePathRoot)) {
throw new IllegalArgumentException("no attachmentPath defined");
}
// create destination folder // create destination folder
StringBuilder msgfile = new StringBuilder().append(archivePathRoot) StringBuilder msgfile = new StringBuilder().append(archivePathRoot)
.append(archive.getArchivePathSnippet()); .append(archive.getArchivePathSnippet());
@ -203,6 +211,7 @@ public class ArchiveFileSystem implements IArchiveStore {
} catch (Exception e) { } catch (Exception e) {
log.log(Level.SEVERE, "saveLOBData", e); log.log(Level.SEVERE, "saveLOBData", e);
archive.setByteData(null); archive.setByteData(null);
throw new RuntimeException(e);
} finally { } finally {
if(out != null){ if(out != null){
try { try {
@ -210,7 +219,6 @@ public class ArchiveFileSystem implements IArchiveStore {
} catch (Exception e) { } } catch (Exception e) { }
} }
} }
} }
private String getArchivePathRoot(MStorageProvider prov) { private String getArchivePathRoot(MStorageProvider prov) {
@ -245,4 +253,17 @@ public class ArchiveFileSystem implements IArchiveStore {
return true; return true;
} }
@Override
public boolean isPendingFlush() {
return buffer != null && buffer.length > 0;
}
@Override
public void flush(MArchive archive, MStorageProvider prov) {
if (buffer != null && buffer.length > 0) {
write(archive, prov, buffer);
buffer = null;
}
}
} }

View File

@ -21,4 +21,7 @@ public interface IArchiveStore {
public boolean deleteArchive(MArchive archive, MStorageProvider prov); public boolean deleteArchive(MArchive archive, MStorageProvider prov);
public boolean isPendingFlush();
public void flush(MArchive archive,MStorageProvider prov);
} }

View File

@ -0,0 +1,27 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 2012 Trek Global *
* 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. *
*****************************************************************************/
package org.compiere.model;
public interface IImageStore {
public byte[] load(MImage image, MStorageProvider prov);
public void save(MImage image, MStorageProvider prov, byte[] inflatedData);
public boolean delete(MImage image, MStorageProvider prov);
public boolean isPendingFlush();
public void flush(MImage image,MStorageProvider prov);
}

View File

@ -449,6 +449,21 @@ public interface I_AD_ClientInfo
public org.compiere.model.I_AD_StorageProvider getStorageArchive() throws RuntimeException; public org.compiere.model.I_AD_StorageProvider getStorageArchive() throws RuntimeException;
/** Column name StorageImage_ID */
public static final String COLUMNNAME_StorageImage_ID = "StorageImage_ID";
/** Set Image Store.
* Storage provider for Image
*/
public void setStorageImage_ID (int StorageImage_ID);
/** Get Image Store.
* Storage provider for Image
*/
public int getStorageImage_ID();
public org.compiere.model.I_AD_StorageProvider getStorageImage() throws RuntimeException;
/** Column name Updated */ /** Column name Updated */
public static final String COLUMNNAME_Updated = "Updated"; public static final String COLUMNNAME_Updated = "Updated";

View File

@ -0,0 +1,125 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 2012 Trek Global *
* 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. *
*****************************************************************************/
package org.compiere.model;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.logging.Level;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.compiere.util.CLogger;
/**
* @author hengsin
*
*/
public class ImageDBStorageImpl implements IImageStore {
private final CLogger log = CLogger.getCLogger(getClass());
@Override
public byte[] load(MImage image, MStorageProvider prov) {
byte[] deflatedData = image.getByteData();
if (deflatedData == null)
return null;
//
if (log.isLoggable(Level.FINE)) log.fine("ZipSize=" + deflatedData.length);
if (deflatedData.length == 0)
return null;
byte[] inflatedData = null;
try {
ByteArrayInputStream in = new ByteArrayInputStream(deflatedData);
ZipInputStream zip = new ZipInputStream(in);
ZipEntry entry = zip.getNextEntry();
if (entry != null) // just one entry
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int length = zip.read(buffer);
while (length != -1) {
out.write(buffer, 0, length);
length = zip.read(buffer);
}
//
inflatedData = out.toByteArray();
if (log.isLoggable(Level.FINE)) log.fine("Size=" + inflatedData.length + " - zip=" + entry.getCompressedSize()
+ "(" + entry.getSize() + ") "
+ (entry.getCompressedSize() * 100 / entry.getSize()) + "%");
} else {
//not zip stream, legacy data
inflatedData = deflatedData;
}
} catch (Exception e) {
log.log(Level.SEVERE, "", e);
inflatedData = null;
}
return inflatedData;
}
@Override
public void save(MImage image, MStorageProvider prov,byte[] inflatedData) {
if (inflatedData == null || inflatedData.length == 0) {
image.setByteData(null);
return;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(out);
zip.setMethod(ZipOutputStream.DEFLATED);
zip.setLevel(Deflater.BEST_COMPRESSION);
zip.setComment("idempiere");
//
byte[] deflatedData = null;
try {
ZipEntry entry = new ZipEntry("IdempiereImage");
entry.setTime(System.currentTimeMillis());
entry.setMethod(ZipEntry.DEFLATED);
zip.putNextEntry(entry);
zip.write(inflatedData, 0, inflatedData.length);
zip.closeEntry();
if (log.isLoggable(Level.FINE)) log.fine(entry.getCompressedSize() + " (" + entry.getSize() + ") "
+ (entry.getCompressedSize() * 100 / entry.getSize()) + "%");
//
// zip.finish();
zip.close();
deflatedData = out.toByteArray();
if (log.isLoggable(Level.FINE)) log.fine("Length=" + inflatedData.length);
} catch (Exception e) {
log.log(Level.SEVERE, "saveLOBData", e);
deflatedData = null;
}
image.setByteData(deflatedData);
}
@Override
public boolean delete(MImage image, MStorageProvider prov) {
return true;
}
@Override
public boolean isPendingFlush() {
return false;
}
@Override
public void flush(MImage image, MStorageProvider prov) {
}
}

View File

@ -0,0 +1,278 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 2012 Trek Global *
* 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. *
*****************************************************************************/
package org.compiere.model;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import org.compiere.util.CLogger;
import org.compiere.util.Util;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.w3c.dom.Element;
/**
* @author hengsin
*
*/
public class ImageFileStorageImpl implements IImageStore {
private String IMAGE_FOLDER_PLACEHOLDER = "%IMAGE_FOLDER%";
private final CLogger log = CLogger.getCLogger(getClass());
//temporary buffer when AD_Image_ID=0
private byte[] buffer = null;
@Override
public byte[] load(MImage image, MStorageProvider prov) {
String imagePathRoot = getImagePathRoot(prov);
if ("".equals(imagePathRoot)) {
throw new IllegalArgumentException("no path defined");
}
buffer = null;
byte[] data = image.getByteData();
if (data == null) {
return null;
}
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
final DocumentBuilder builder = factory.newDocumentBuilder();
final Document document = builder.parse(new ByteArrayInputStream(data));
final NodeList entries = document.getElementsByTagName("entry");
if(entries.getLength()!=1){
log.severe("no image entry found");
}
final Node entryNode = entries.item(0);
final NamedNodeMap attributes = entryNode.getAttributes();
final Node fileNode = attributes.getNamedItem("file");
if(fileNode==null ){
log.severe("no filename for entry");
return null;
}
String filePath = fileNode.getNodeValue();
if (log.isLoggable(Level.FINE)) log.fine("filePath: " + filePath);
if(filePath!=null){
filePath = filePath.replaceFirst(IMAGE_FOLDER_PLACEHOLDER, imagePathRoot.replaceAll("\\\\","\\\\\\\\"));
//just to be shure...
String replaceSeparator = File.separator;
if(!replaceSeparator.equals("/")){
replaceSeparator = "\\\\";
}
filePath = filePath.replaceAll("/", replaceSeparator);
filePath = filePath.replaceAll("\\\\", replaceSeparator);
}
if (log.isLoggable(Level.FINE)) log.fine("filePath: " + filePath);
final File file = new File(filePath);
if (file.exists()) {
// read files into byte[]
final byte[] dataEntry = new byte[(int) file.length()];
try {
final FileInputStream fileInputStream = new FileInputStream(file);
fileInputStream.read(dataEntry);
fileInputStream.close();
} catch (FileNotFoundException e) {
log.severe("File Not Found.");
e.printStackTrace();
} catch (IOException e1) {
log.severe("Error Reading The File.");
e1.printStackTrace();
}
return dataEntry;
} else {
log.severe("file not found: " + file.getAbsolutePath());
return null;
}
} catch (SAXException sxe) {
// Error generated during parsing)
Exception x = sxe;
if (sxe.getException() != null)
x = sxe.getException();
x.printStackTrace();
log.severe(x.getMessage());
} catch (ParserConfigurationException pce) {
// Parser with specified options can't be built
pce.printStackTrace();
log.severe(pce.getMessage());
} catch (IOException ioe) {
// I/O error
ioe.printStackTrace();
log.severe(ioe.getMessage());
}
return null;
}
@Override
public void save(MImage image, MStorageProvider prov,byte[] inflatedData) {
if (inflatedData == null || inflatedData.length == 0) {
image.setByteData(null);
delete(image, prov);
return;
}
if(image.get_ID()==0){
//set binary data otherwise save will fail
image.setByteData(new byte[]{'0'});
buffer = inflatedData;
} else {
write(image, prov, inflatedData);
}
}
private void write(MImage image, MStorageProvider prov, byte[] inflatedData) {
BufferedOutputStream out = null;
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
String imagePathRoot = getImagePathRoot(prov);
if ("".equals(imagePathRoot)) {
throw new IllegalArgumentException("no storage path defined");
}
// create destination folder
StringBuilder msgfile = new StringBuilder().append(imagePathRoot)
.append(image.getImageStoragePath());
final File destFolder = new File(msgfile.toString());
if (!destFolder.exists()) {
if (!destFolder.mkdirs()) {
log.warning("unable to create folder: " + destFolder.getPath());
}
}
String ext = getExtension(image);
// write to path
msgfile = new StringBuilder().append(imagePathRoot).append(File.separator)
.append(image.getImageStoragePath()).append(image.get_ID()).append(ext);
final File destFile = new File(msgfile.toString());
out = new BufferedOutputStream(new FileOutputStream(destFile));
out.write(inflatedData);
out.flush();
//create xml entry
final DocumentBuilder builder = factory.newDocumentBuilder();
final Document document = builder.newDocument();
final Element root = document.createElement("image");
document.appendChild(root);
document.setXmlStandalone(true);
final Element entry = document.createElement("entry");
StringBuilder msgsat = new StringBuilder(IMAGE_FOLDER_PLACEHOLDER).append(image.getImageStoragePath()).append(image.get_ID()).append(ext);
entry.setAttribute("file", msgsat.toString());
root.appendChild(entry);
final Source source = new DOMSource(document);
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
final Result result = new StreamResult(bos);
final Transformer xformer = TransformerFactory.newInstance().newTransformer();
xformer.transform(source, result);
final byte[] xmlData = bos.toByteArray();
if (log.isLoggable(Level.FINE)) log.fine(bos.toString());
//store xml in db
image.setByteData(xmlData);
} catch (Exception e) {
log.log(Level.SEVERE, "saveLOBData", e);
image.setByteData(null);
throw new RuntimeException(e);
} finally {
if(out != null){
try {
out.close();
} catch (Exception e) { }
}
}
}
private String getImagePathRoot(MStorageProvider prov) {
String imagePathRoot = prov.getFolder();
if (imagePathRoot == null)
imagePathRoot = "";
if (Util.isEmpty(imagePathRoot)) {
log.severe("no image Path defined");
} else if (!imagePathRoot.endsWith(File.separator)){
imagePathRoot = imagePathRoot + File.separator;
log.fine(imagePathRoot);
}
return imagePathRoot;
}
@Override
public boolean delete(MImage image, MStorageProvider prov) {
String imagePathRoot = getImagePathRoot(prov);
if ("".equals(imagePathRoot)) {
throw new IllegalArgumentException("no image path defined");
}
String ext = getExtension(image);
StringBuilder msgfile = new StringBuilder().append(imagePathRoot)
.append(image.getImageStoragePath()).append(image.get_ID()).append(ext);
File file=new File(msgfile.toString());
if (file !=null && file.exists()) {
if (!file.delete()) {
log.warning("unable to delete " + file.getAbsolutePath());
return false;
}
}
return true;
}
private String getExtension(MImage image) {
String name = image.getName();
String ext = "";
if (name.lastIndexOf(".") > 0) {
ext = name.substring(name.lastIndexOf("."));
}
return ext;
}
@Override
public boolean isPendingFlush() {
return buffer != null && buffer.length > 0;
}
@Override
public void flush(MImage image, MStorageProvider prov) {
if (buffer != null && buffer.length > 0) {
write(image, prov, buffer);
buffer = null;
}
}
}

View File

@ -278,4 +278,11 @@ public class MArchive extends X_AD_Archive {
} }
@Override
protected void saveNew_afterSetID()
{
IArchiveStore prov = provider.getArchiveStore();
if (prov != null && prov.isPendingFlush())
prov.flush(this,provider);
}
} // MArchive } // MArchive

View File

@ -21,6 +21,7 @@ import java.awt.Image;
import java.awt.MediaTracker; import java.awt.MediaTracker;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
@ -49,6 +50,8 @@ public class MImage extends X_AD_Image
*/ */
private static final long serialVersionUID = -7361463683427300715L; private static final long serialVersionUID = -7361463683427300715L;
private MStorageProvider provider;
/** /**
* Get MImage from Cache * Get MImage from Cache
* @param ctx context * @param ctx context
@ -84,6 +87,7 @@ public class MImage extends X_AD_Image
super (ctx, AD_Image_ID, trxName); super (ctx, AD_Image_ID, trxName);
if (AD_Image_ID < 1) if (AD_Image_ID < 1)
setName("-"); setName("-");
initImageStoreDetails(ctx, trxName);
} // MImage } // MImage
/** /**
@ -95,6 +99,7 @@ public class MImage extends X_AD_Image
public MImage (Properties ctx, ResultSet rs, String trxName) public MImage (Properties ctx, ResultSet rs, String trxName)
{ {
super(ctx, rs, trxName); super(ctx, rs, trxName);
initImageStoreDetails(ctx, trxName);
} // MImage } // MImage
@ -233,20 +238,31 @@ public class MImage extends X_AD_Image
* Set Binary Data * Set Binary Data
* @param BinaryData data * @param BinaryData data
*/ */
@Override
public void setBinaryData (byte[] BinaryData) public void setBinaryData (byte[] BinaryData)
{ {
m_image = null; m_image = null;
m_icon = null; m_icon = null;
super.setBinaryData (BinaryData); IImageStore prov = provider.getImageStore();
if (prov != null)
prov.save(this,provider,BinaryData);
} // setBinaryData } // setBinaryData
@Override
public byte[] getBinaryData() {
IImageStore prov = provider.getImageStore();
if (prov != null)
return prov.load(this,provider);
return null;
}
/** /**
* Get Data * Get Data
* @return data * @return data
*/ */
public byte[] getData() public byte[] getData()
{ {
byte[] data = super.getBinaryData (); byte[] data = getBinaryData ();
if (data != null) if (data != null)
return data; return data;
// From URL // From URL
@ -311,5 +327,50 @@ public class MImage extends X_AD_Image
setAD_Org_ID(0); setAD_Org_ID(0);
return true; return true;
} // beforeSave } // beforeSave
public String getImageStoragePath() {
StringBuilder path = new StringBuilder().append(this.getAD_Client_ID()).append(File.separator).append(this.getAD_Org_ID())
.append(File.separator);
return path.toString();
}
/**
* @param ctx
* @param trxName
*/
private void initImageStoreDetails(Properties ctx, String trxName) {
MClientInfo clientInfo = MClientInfo.get(ctx);
provider=new MStorageProvider(ctx, clientInfo.getStorageImage_ID(), trxName);
}
public void setStorageProvider(MStorageProvider p) {
provider = p;
}
public byte[] getByteData(){
return super.getBinaryData();
}
public void setByteData(byte[] BinaryData){
super.setBinaryData(BinaryData);
}
@Override
protected boolean afterDelete (boolean success) {
if (success) {
IImageStore prov = provider.getImageStore();
if (prov != null)
return prov.delete(this,provider);
}
return success;
}
@Override
protected void saveNew_afterSetID()
{
IImageStore prov = provider.getImageStore();
if (prov != null && prov.isPendingFlush())
prov.flush(this, provider);
}
} // MImage } // MImage

View File

@ -63,4 +63,16 @@ public class MStorageProvider extends X_AD_StorageProvider {
return store; return store;
} }
public IImageStore getImageStore() {
ServiceQuery query=new ServiceQuery();
String method = this.getMethod();
if (method == null)
method = "DB";
query.put("method", method);
IImageStore store = Service.locator().locate(IImageStore.class, query).getService();
if (store == null) {
throw new AdempiereException("No image storage provider found");
}
return store;
}
} }

View File

@ -2782,6 +2782,7 @@ public abstract class PO
} }
m_IDs[0] = Integer.valueOf(no); m_IDs[0] = Integer.valueOf(no);
set_ValueNoCheck(m_KeyColumns[0], m_IDs[0]); set_ValueNoCheck(m_KeyColumns[0], m_IDs[0]);
saveNew_afterSetID();
} }
//uuid secondary key //uuid secondary key
int uuidIndex = p_info.getColumnIndex(getUUIDColumnName()); int uuidIndex = p_info.getColumnIndex(getUUIDColumnName());
@ -3096,6 +3097,13 @@ public abstract class PO
return 0; return 0;
} // saveNew_getID } // saveNew_getID
/**
* Call after ID have been assigned for new record
*/
protected void saveNew_afterSetID()
{
}
/** /**
* Create Single/Multi Key Where Clause * Create Single/Multi Key Where Clause

View File

@ -109,15 +109,27 @@ public class PO_Record
if (s_cascades[i] != AD_Table_ID) if (s_cascades[i] != AD_Table_ID)
{ {
Object[] params = new Object[]{Integer.valueOf(AD_Table_ID), Integer.valueOf(Record_ID)}; Object[] params = new Object[]{Integer.valueOf(AD_Table_ID), Integer.valueOf(Record_ID)};
StringBuffer sql = new StringBuffer ("DELETE FROM ") if (s_cascadeNames[i].equals(X_AD_Attachment.Table_Name) || s_cascadeNames[i].equals(X_AD_Archive.Table_Name))
.append(s_cascadeNames[i]) {
.append(" WHERE AD_Table_ID=? AND Record_ID=?"); Query query = new Query(Env.getCtx(), s_cascadeNames[i], "AD_Table_ID=? AND Record_ID=?", trxName);
int no = DB.executeUpdate(sql.toString(), params, false, trxName); List<PO> list = query.setParameters(params).list();
if (no > 0) { for(PO po : list)
if (log.isLoggable(Level.CONFIG)) log.config(s_cascadeNames[i] + " (" + AD_Table_ID + "/" + Record_ID + ") #" + no); {
} else if (no < 0) { po.deleteEx(true);
log.severe(s_cascadeNames[i] + " (" + AD_Table_ID + "/" + Record_ID + ") #" + no); }
return false; }
else
{
StringBuffer sql = new StringBuffer ("DELETE FROM ")
.append(s_cascadeNames[i])
.append(" WHERE AD_Table_ID=? AND Record_ID=?");
int no = DB.executeUpdate(sql.toString(), params, false, trxName);
if (no > 0) {
if (log.isLoggable(Level.CONFIG)) log.config(s_cascadeNames[i] + " (" + AD_Table_ID + "/" + Record_ID + ") #" + no);
} else if (no < 0) {
log.severe(s_cascadeNames[i] + " (" + AD_Table_ID + "/" + Record_ID + ") #" + no);
return false;
}
} }
} }
} }

View File

@ -30,7 +30,7 @@ public class X_AD_ClientInfo extends PO implements I_AD_ClientInfo, I_Persistent
/** /**
* *
*/ */
private static final long serialVersionUID = 20191121L; private static final long serialVersionUID = 20200226L;
/** Standard Constructor */ /** Standard Constructor */
public X_AD_ClientInfo (Properties ctx, int AD_ClientInfo_ID, String trxName) public X_AD_ClientInfo (Properties ctx, int AD_ClientInfo_ID, String trxName)
@ -767,4 +767,32 @@ public class X_AD_ClientInfo extends PO implements I_AD_ClientInfo, I_Persistent
return 0; return 0;
return ii.intValue(); return ii.intValue();
} }
public org.compiere.model.I_AD_StorageProvider getStorageImage() throws RuntimeException
{
return (org.compiere.model.I_AD_StorageProvider)MTable.get(getCtx(), org.compiere.model.I_AD_StorageProvider.Table_Name)
.getPO(getStorageImage_ID(), get_TrxName()); }
/** Set Image Store.
@param StorageImage_ID
Storage provider for Image
*/
public void setStorageImage_ID (int StorageImage_ID)
{
if (StorageImage_ID < 1)
set_Value (COLUMNNAME_StorageImage_ID, null);
else
set_Value (COLUMNNAME_StorageImage_ID, Integer.valueOf(StorageImage_ID));
}
/** Get Image Store.
@return Storage provider for Image
*/
public int getStorageImage_ID ()
{
Integer ii = (Integer)get_Value(COLUMNNAME_StorageImage_ID);
if (ii == null)
return 0;
return ii.intValue();
}
} }