From 4966a85ed6f794f23792aa8188979537778fae46 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Date: Tue, 23 Jan 2024 11:03:20 -0800 Subject: [PATCH] 1.2.0 url access (#40) * Add description * Add label --- .gitignore | 2 + README.md | 18 ++- pom.xml | 4 +- .../camunda/filestorage/FileRepoFactory.java | 1 + .../io/camunda/filestorage/FileVariable.java | 151 +++++++++-------- .../filestorage/FileVariableReference.java | 45 ++++-- .../filestorage/StorageDefinition.java | 153 ++++++++++++------ .../filestorage/StorageTempFolder.java | 27 ++-- .../io/camunda/filestorage/StorageURL.java | 95 +++++++++++ .../filestorage/cmis/CmisConnection.java | 9 +- 10 files changed, 356 insertions(+), 149 deletions(-) create mode 100644 .gitignore create mode 100644 src/main/java/io/camunda/filestorage/StorageURL.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bba7b53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target/ +/.idea/ diff --git a/README.md b/README.md index 81a4823..15ac7e8 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # zebee-cherry-filestorage +[![](https://img.shields.io/badge/Community%20Extension-An%20open%20source%20community%20maintained%20project-FF4700)](https://github.com/camunda-community-hub/community) +![Compatible with: Camunda 8](https://img.shields.io/badge/Compatible%20with-Camunda%208-0072Ce) Library to manipulate File process variable, and store the content in different storage (Folder, CMIS...) -This library is used by connectors. There is three main function +This library is used by connectors. # Store file to the storage @@ -51,7 +53,7 @@ The fileVariableReference is only a reference, and can be saved in the process v # Read file from the storage -To read a file from the storage, the first operation consist to access the FileVariableReference +To read a file from the storage, the first operation consist of accessing the FileVariableReference ```` String processVariableSt = @@ -137,3 +139,15 @@ import StorageCMIS; StorageCMIS.getStorageDefinitionString(String url, String repositoryName, String userName, String password, String storageDefinitionFolder) ```` + +## URL +The file may be access via an URL. This storage is use to READ only. + +For example, this soure file read a document from an URL. +`````json +{ + "storageDefinition": "URL", + "content": "https://github.com/camunda-community-hub/camunda-8-connector-officetopdf/raw/main/src/test/resources/OfficeToPdfExample.docx" +} + +````` \ No newline at end of file diff --git a/pom.xml b/pom.xml index f4166cd..1e5b964 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.camunda.filestorage filestorage - 1.1.0 + 1.2.0 17 @@ -36,7 +36,7 @@ org.camunda.community community-hub-release-parent - 1.3.1 + 1.4.2 diff --git a/src/main/java/io/camunda/filestorage/FileRepoFactory.java b/src/main/java/io/camunda/filestorage/FileRepoFactory.java index 4735517..abff796 100644 --- a/src/main/java/io/camunda/filestorage/FileRepoFactory.java +++ b/src/main/java/io/camunda/filestorage/FileRepoFactory.java @@ -131,6 +131,7 @@ private Storage getStorage(StorageDefinition storageDefinition) throws Exception case FOLDER -> new StorageFolder(storageDefinition, this); case CMIS -> new StorageCMIS(storageDefinition, this); case TEMPFOLDER -> new StorageTempFolder(storageDefinition, this); + case URL -> new StorageURL(storageDefinition, this); }; } } diff --git a/src/main/java/io/camunda/filestorage/FileVariable.java b/src/main/java/io/camunda/filestorage/FileVariable.java index aab3d66..856df89 100644 --- a/src/main/java/io/camunda/filestorage/FileVariable.java +++ b/src/main/java/io/camunda/filestorage/FileVariable.java @@ -7,87 +7,104 @@ package io.camunda.filestorage; import java.io.File; -import java.net.URLConnection; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; /* Definition of a FileVariable */ public class FileVariable { - private String name; - private String mimeType; - private byte[] value; - - /** - * Keep the information from where this fileVariable come from. - * So, if the worker wants to save it at the same place, it has the information. - * This is only an information from the FileVariable, it may be null - */ - private StorageDefinition storageDefinition; - - /** - * The default connectors exist to let the Json deserializer create it - */ - public FileVariable() { + private String name; + private String mimeType; + private byte[] value; - } - /** - * To load / create a file Variable, go to the FileVariableFactory - */ - protected FileVariable(StorageDefinition storageDefinition) { - this.storageDefinition = storageDefinition; - } + /** + * Keep the information from where this fileVariable come from. + * So, if the worker wants to save it at the same place, it has the information. + * This is only an information from the FileVariable, it may be null + */ + private StorageDefinition storageDefinition; - public String getName() { - return name; - } + /** + * The default connectors exist to let the Json deserializer create it + */ + public FileVariable() { - public String getMimeType() { - return mimeType; - } + } - public byte[] getValue() { - return value; - } + /** + * To load / create a file Variable, go to the FileVariableFactory + */ + protected FileVariable(StorageDefinition storageDefinition) { + this.storageDefinition = storageDefinition; + } - public void setName(String name) { - this.name = name; - } + public String getName() { + return name; + } - public void setMimeType(String mimeType) { - this.mimeType = mimeType; - } + public String getMimeType() { + return mimeType; + } - public void setValue(byte[] value) { - this.value = value; - } + public byte[] getValue() { + return value; + } - public void setStorageDefinition(StorageDefinition storageDefinition) { - this.storageDefinition = storageDefinition; - } - public StorageDefinition getStorageDefinition() { - return storageDefinition; - } + public void setName(String name) { + this.name = name; + } + + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + public void setValue(byte[] value) { + this.value = value; + } - /** - * Return the suffix of the file, based on the name or on the mimeType - * - * @return the suffix - */ - public static String getSuffix(String fileName) { - if (fileName != null) { - int lastDot = fileName.lastIndexOf("."); - if (lastDot != -1) - return fileName.substring(lastDot + 1); - } - return ""; + public void setStorageDefinition(StorageDefinition storageDefinition) { + this.storageDefinition = storageDefinition; + } + + public StorageDefinition getStorageDefinition() { + return storageDefinition; + } + + /** + * Return the suffix of the file, based on the name or on the mimeType + * + * @return the suffix + */ + public static String getSuffix(String fileName) { + if (fileName != null) { + int lastDot = fileName.lastIndexOf("."); + if (lastDot != -1) + return fileName.substring(lastDot + 1); } + return ""; + } - /** - * return the Mimetype from the name. - * - * @return the mime type - */ - public static String getMimeTypeFromName(String fileName) { - File file = new File(fileName); - return URLConnection.guessContentTypeFromName(file.getName()); + /** + * return the Mimetype from the fileName. + * + * @param fileName file name. Must exist/accessible to be transformed to a File + * @return the mime type + */ + public static String getMimeTypeFromName(String fileName) { + return getMimeTypeFromPath(new File(fileName).toPath()); + } + /** + * Return the MimeType from a Path + * @param path path to the information + * @return the mime type + */ + public static String getMimeTypeFromPath(Path path) { + try { + return Files.probeContentType(path); + } catch (IOException var3) { + return null; } + } + } diff --git a/src/main/java/io/camunda/filestorage/FileVariableReference.java b/src/main/java/io/camunda/filestorage/FileVariableReference.java index 1a43ef8..3641e89 100644 --- a/src/main/java/io/camunda/filestorage/FileVariableReference.java +++ b/src/main/java/io/camunda/filestorage/FileVariableReference.java @@ -39,8 +39,17 @@ public class FileVariableReference { public static FileVariableReference fromJson(String fileReferenceJson) throws Exception { try { return new ObjectMapper().readValue(fileReferenceJson, FileVariableReference.class); - } catch (JsonProcessingException e) { - logger.error("FileStorage.FileVariableReference.fromJson: exception " + e + " During un serialize fileVariable"); + } catch (Exception e) { + } + // do a second tentative before logging + String fileReferenceJsonWithoutBackslash = fileReferenceJson.replace("\\\"", "\""); + try { + // if the value is given explicitly, the modeler impose to \ each ", so we have to replace all \" by " + return new ObjectMapper().readValue(fileReferenceJsonWithoutBackslash, FileVariableReference.class); + } catch (Exception e) { + // then now we have to log the error + logger.error("FileStorage.FileVariableReference.fromJson:" + e + " During UnSerialize[" + fileReferenceJson + + "], secondTentative[" + fileReferenceJsonWithoutBackslash + "]"); throw e; } } @@ -70,17 +79,27 @@ public Object getContent() { /** * Must be static to not make any trouble in the serialization/deserialization - * @param fieldReference field reference to get the identification - * @return an indentification, to log it for example + * + * @return information, to log it for example */ - public static String getIdentification(FileVariableReference fieldReference) { - StringBuilder result = new StringBuilder(); - result.append(fieldReference.storageDefinition); - result.append( ": "); - if (fieldReference.content==null) - result.append("null"); - else - result.append((fieldReference.content.toString() + " ").substring(0, 50)); - return result.toString(); + public static String getInformation(FileVariableReference fileVariableReference) { + StringBuilder result = new StringBuilder(); + try { + StorageDefinition storageDefinition = StorageDefinition.getFromString(fileVariableReference.storageDefinition); + result.append(storageDefinition.getInformation()); + } catch (Exception e) { + result.append("Can't get storageDefinition from ["); + result.append(fileVariableReference.storageDefinition); + result.append("] : "); + result.append(e.getMessage()); + } + result.append(": "); + if (fileVariableReference.content == null) + result.append("null"); + else if (fileVariableReference.content.toString().length() < 100) + result.append(fileVariableReference.content.toString()); + else + result.append(fileVariableReference.content.toString().substring(0, 100)); + return result.toString(); } } diff --git a/src/main/java/io/camunda/filestorage/StorageDefinition.java b/src/main/java/io/camunda/filestorage/StorageDefinition.java index cfcbe5b..15e90a8 100644 --- a/src/main/java/io/camunda/filestorage/StorageDefinition.java +++ b/src/main/java/io/camunda/filestorage/StorageDefinition.java @@ -12,71 +12,118 @@ package io.camunda.filestorage; import com.google.gson.Gson; +import io.camunda.filestorage.cmis.CmisParameters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class StorageDefinition { - public final static String ERROR_INCORRECT_STORAGEDEFINITION = "INCORRECT_STORAGEDEFINITION"; - public static final String STORAGE_DEFINITION_DELIMITATEUR = ":"; - static Logger logger = LoggerFactory.getLogger(StorageDefinition.class.getName()); - public StorageDefinitionType type; - public String complement = null; - public Object complementInObject = null; + public final static String ERROR_INCORRECT_STORAGEDEFINITION = "INCORRECT_STORAGEDEFINITION"; + public static final String STORAGE_DEFINITION_DELIMITATEUR = ":"; + static Logger logger = LoggerFactory.getLogger(StorageDefinition.class.getName()); + public StorageDefinitionType type; + public String complement = null; + public Object complementInObject = null; - public static StorageDefinition getFromString(String completeStorageDefinition) throws Exception { - try { - int posDelimiter = completeStorageDefinition.indexOf(STORAGE_DEFINITION_DELIMITATEUR); + public static StorageDefinition getFromString(String completeStorageDefinition) throws Exception { + try { + int posDelimiter = completeStorageDefinition.indexOf(STORAGE_DEFINITION_DELIMITATEUR); - String storageTypeSt = posDelimiter == -1 ? completeStorageDefinition : completeStorageDefinition.substring(0, posDelimiter); - StorageDefinition storageDefinition = new StorageDefinition(); - storageDefinition.type = StorageDefinitionType.valueOf(storageTypeSt); + String storageTypeSt = + posDelimiter == -1 ? completeStorageDefinition : completeStorageDefinition.substring(0, posDelimiter); + StorageDefinition storageDefinition = new StorageDefinition(); + storageDefinition.type = StorageDefinitionType.valueOf(storageTypeSt); - switch( storageDefinition.type) { - case FOLDER: - storageDefinition.complement = completeStorageDefinition.substring(posDelimiter + 1); - break; - case CMIS: - String complement = completeStorageDefinition.substring(posDelimiter + 1); - Gson gson = new Gson(); - storageDefinition.complementInObject= gson.fromJson(complement, Object.class); - break; - default: - break; - } + switch (storageDefinition.type) { + case FOLDER: + storageDefinition.complement = completeStorageDefinition.substring(posDelimiter + 1); + break; + case CMIS: + String complement = completeStorageDefinition.substring(posDelimiter + 1); + Gson gson = new Gson(); + storageDefinition.complementInObject = gson.fromJson(complement, Object.class); + break; + default: + break; + } - return storageDefinition; - } catch (Exception e) { - String message = "Can't decode storageDefinition [" + completeStorageDefinition + "]. Format should be [" - + StorageDefinitionType.JSON - + "|" + StorageDefinitionType.CMIS - + "|" + StorageDefinitionType.TEMPFOLDER - + "|" + StorageDefinitionType.FOLDER + "]"; - logger.error("StorageDefinition: Can't decode [" + completeStorageDefinition + "] " + e); - throw new Exception(ERROR_INCORRECT_STORAGEDEFINITION+": "+message); - } + return storageDefinition; + } catch (Exception e) { + String message = "Can't decode storageDefinition [" + completeStorageDefinition + "]. Format should be [" + + StorageDefinitionType.JSON + "|" + StorageDefinitionType.CMIS + "|" + StorageDefinitionType.TEMPFOLDER + "|" + + StorageDefinitionType.FOLDER + "]"; + logger.error("StorageDefinition: Can't decode [" + completeStorageDefinition + "] " + e); + throw new Exception(ERROR_INCORRECT_STORAGEDEFINITION + ": " + message); } + } - /** - * Encode the current storage definition to a String, so it is easily movable to any information - * - * @return the string which encode the storage definition - */ - public String encodeToString() { - String result = type.toString(); - if (complement != null) { - result += STORAGE_DEFINITION_DELIMITATEUR + complement; - } else if (complementInObject != null) { - Gson gson = new Gson(); - result += STORAGE_DEFINITION_DELIMITATEUR + gson.toJson(complementInObject); - } - return result; + /** + * Encode the current storage definition to a String, so it is easily movable to any information + * + * @return the string which encode the storage definition + */ + public String encodeToString() { + String result = type.toString(); + if (complement != null) { + result += STORAGE_DEFINITION_DELIMITATEUR + complement; + } else if (complementInObject != null) { + Gson gson = new Gson(); + result += STORAGE_DEFINITION_DELIMITATEUR + gson.toJson(complementInObject); } + return result; + } - /** - * Define how the file variable is stored. - * JSON: easy, but attention to large file - */ - public enum StorageDefinitionType {JSON, TEMPFOLDER, FOLDER, CMIS} + /** + * return information on the storage, to log it for example + * + * @return information on the storageDefinition + */ + public String getInformation() { + StringBuilder info = new StringBuilder(); + info.append(type.toString()); + switch (type) { + case FOLDER: + info.append(": folder["); + info.append(complement); + info.append("["); + break; + + case CMIS: + try { + CmisParameters cmisParameters = CmisParameters.getCodingConnection(complementInObject); + info.append(": url["); + info.append(cmisParameters.url); + info.append("] respitory["); + info.append(cmisParameters.repositoryName); + info.append("] userName["); + info.append(cmisParameters.userName); + info.append("]"); + } catch (Exception e) { + info.append("Can't decode Parameter " + e.getMessage()); + } + break; + + case TEMPFOLDER: + info.append(": folder["); + info.append(StorageTempFolder.getTempFolder()); + info.append("]"); + break; + + case URL: + info.append(": url provided in file"); + break; + + case JSON: + info.append(""); + break; + } + return info.toString(); + } + + /** + * Define how the file variable is stored. + * JSON: easy, but attention to large file + */ + public enum StorageDefinitionType {JSON, TEMPFOLDER, FOLDER, CMIS, URL} } diff --git a/src/main/java/io/camunda/filestorage/StorageTempFolder.java b/src/main/java/io/camunda/filestorage/StorageTempFolder.java index fe0dd67..ba6dd57 100644 --- a/src/main/java/io/camunda/filestorage/StorageTempFolder.java +++ b/src/main/java/io/camunda/filestorage/StorageTempFolder.java @@ -19,8 +19,6 @@ public class StorageTempFolder extends Storage { Logger logger = LoggerFactory.getLogger(StorageTempFolder.class.getName()); - private FileRepoFactory fileRepoFactory; - protected StorageTempFolder(StorageDefinition storageDefinition, FileRepoFactory fileRepoFactory) { super(storageDefinition, fileRepoFactory); } @@ -61,7 +59,7 @@ public FileVariableReference toStorage(FileVariable fileVariable, FileVariableRe return fileVariableReferenceOutput; } catch (Exception e) { - logger.error(fileRepoFactory.getLoggerHeaderMessage(StorageTempFolder.class) + "Exception " + e + logger.error(getFileRepoFactory().getLoggerHeaderMessage(StorageTempFolder.class) + "Exception " + e + " During write fileVariable on tempFolder[" + tempPath + "]"); throw e; } @@ -75,27 +73,26 @@ public FileVariableReference toStorage(FileVariable fileVariable, FileVariableRe * @throws Exception during the writing */ public FileVariable fromStorage(FileVariableReference fileVariableReference) throws Exception { - String tempFilePath; try { // get the temporary path - Path tempPath = Files.createTempFile("", ""); + Path tempFolder = getTempFolder(); String separator = FileSystems.getDefault().getSeparator(); - tempFilePath = tempPath.toString().substring(0, tempPath.toString().lastIndexOf(separator)); - FileVariable fileVariable = new FileVariable(getStorageDefinition()); fileVariable.setName(fileVariableReference.content.toString()); fileVariable.setMimeType(FileVariable.getMimeTypeFromName(fileVariableReference.content.toString())); - fileVariable.setValue(Files.readAllBytes(Paths.get(tempFilePath + separator + fileVariableReference.content.toString()))); + fileVariable.setValue(Files.readAllBytes(Paths.get(tempFolder.toString() + separator + fileVariableReference.content.toString()))); return fileVariable; } catch (Exception e) { logger.error( - fileRepoFactory.getLoggerHeaderMessage(StorageTempFolder.class) + "Exception " + e + " During read file[" + getFileRepoFactory().getLoggerHeaderMessage(StorageTempFolder.class) + "Exception " + e + " During read file[" + fileVariableReference.content.toString() + "] in temporaryPath[" + fileVariableReference.content.toString() + "]"); throw e; } } + + /** * Delete the file * @@ -104,11 +101,19 @@ public FileVariable fromStorage(FileVariableReference fileVariableReference) thr */ public boolean purgeStorage( FileVariableReference fileVariableReference) { - String tmpDir = System.getProperty("java.io.tmpdir"); - File file = new File(tmpDir + FileSystems.getDefault().getSeparator() + fileVariableReference.getContent().toString()); + Path tempFolder = getTempFolder(); + File file = new File(tempFolder.toString() + FileSystems.getDefault().getSeparator() + fileVariableReference.getContent().toString()); if (file.exists()) return file.delete(); return true; } + /** + * get the temporary path + * @return the temporary path on this host + */ + public static Path getTempFolder() { + String tmpDir = System.getProperty("java.io.tmpdir"); + return Paths.get(tmpDir); + } } diff --git a/src/main/java/io/camunda/filestorage/StorageURL.java b/src/main/java/io/camunda/filestorage/StorageURL.java new file mode 100644 index 0000000..4b53029 --- /dev/null +++ b/src/main/java/io/camunda/filestorage/StorageURL.java @@ -0,0 +1,95 @@ +package io.camunda.filestorage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class StorageURL extends Storage { + Logger logger = LoggerFactory.getLogger(StorageURL.class.getName()); + + + protected StorageURL(StorageDefinition storageDefinition, FileRepoFactory fileRepoFactory) { + super(storageDefinition, fileRepoFactory); + } + + @Override + public String getName() { + return "URL"; + } + + public static String getStorageDefinitionString() { + return StorageDefinition.StorageDefinitionType.URL.toString(); + } + + /** + * Save the file Variable structure in the temporary folder + * + * @param fileVariable fileVariable to save it + * @param fileVariableReference file variable to update (may be null) + */ + public FileVariableReference toStorage(FileVariable fileVariable, FileVariableReference fileVariableReference) + throws Exception { + throw new Exception("Storage URL work in read only"); + } + + /** + * read the fileVariable + * + * @param fileVariableReference name of the file in the temporary directory + * @return the fileVariable object + * @throws Exception during the writing + */ + public FileVariable fromStorage(FileVariableReference fileVariableReference) throws Exception { + InputStream inputStream = null; + try { + URL url = new URL(fileVariableReference.getContent().toString()); + + // Open a connection to the URL + + inputStream = url.openStream(); + // Define a buffer to read data into + byte[] buffer = new byte[1024]; + int bytesRead; + ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); + + // Read bytes from the input stream and store them in the ByteBuffer + while ((bytesRead = inputStream.read(buffer)) != -1) { + byteBuffer.write(buffer, 0, bytesRead); + } + + FileVariable fileVariable = new FileVariable(getStorageDefinition()); + fileVariable.setName(fileVariableReference.content.toString()); + + Path pathUri = Paths.get(url.toURI().getPath()); + fileVariable.setMimeType(FileVariable.getMimeTypeFromPath(pathUri)); + fileVariable.setValue(byteBuffer.toByteArray()); + return fileVariable; + + } catch (Exception e) { + logger.error(getFileRepoFactory().getLoggerHeaderMessage(StorageURL.class) + "Exception " + e + + " During write fileVariable on Url[" + fileVariableReference.getContent().toString() + "]"); + throw e; + } finally { + if (inputStream != null) + inputStream.close(); + } + + } + + /** + * Delete the file + * + * @param fileVariableReference name of the file in the temporary directory + * @return true if the operation was successful + */ + public boolean purgeStorage(FileVariableReference fileVariableReference) { + // Read only + return false; + } + +} diff --git a/src/main/java/io/camunda/filestorage/cmis/CmisConnection.java b/src/main/java/io/camunda/filestorage/cmis/CmisConnection.java index 8334b27..befd340 100644 --- a/src/main/java/io/camunda/filestorage/cmis/CmisConnection.java +++ b/src/main/java/io/camunda/filestorage/cmis/CmisConnection.java @@ -6,7 +6,14 @@ /* ******************************************************************** */ package io.camunda.filestorage.cmis; -import org.apache.chemistry.opencmis.client.api.*; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.apache.chemistry.opencmis.client.api.Document; +import org.apache.chemistry.opencmis.client.api.DocumentType; +import org.apache.chemistry.opencmis.client.api.Folder; +import org.apache.chemistry.opencmis.client.api.ObjectType; +import org.apache.chemistry.opencmis.client.api.Repository; +import org.apache.chemistry.opencmis.client.api.Session; +import org.apache.chemistry.opencmis.client.api.SessionFactory; import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl; import org.apache.chemistry.opencmis.commons.PropertyIds; import org.apache.chemistry.opencmis.commons.SessionParameter;