Générer un fichier propriétaire de manière récurrente

Bonjour,

J’ai besoin de générer de manière quotidienne un fichier dans un format propriétaire précis (type mainframe, avec insertions d’espaces à la place des données vides, etc.) et de le stocker quelque part pour être récupéré par un autre système (a priori : sur un Amazon S3).

Exemple d’une portion de ligne du fichier à générer :

2018-04-22-22.30.01.921408AD       09510400     09510400RESA ESPANA SA                     DIRECTION EXPORTATION              AVDA DE BURGOS 89                  APARTADO 262

Chaque caractère de la ligne doit correspondre à une donnée en particulier : la date/heure de création du fichier, un identifiant métier, un certain nombre d’espaces, la valeur du champ d’un de mes objets métiers, des espaces, la valeur du champ d’un autre objet métier, etc.

En somme, c’est un peu comme l’inverse d’un adapter. Au lieu d’avoir un fichier en entrée que j’insère dans le modèle métier de Simplicité, je pars du modèle métier de Simplicité et récupère des valeurs que je vais concaténer dans un fichier.

Quelle serait la meilleure manière de mettre ça en place ? Est-ce que je peux mettre le script de génération du fichier dans un share coded que je peux faire exécuter dans la cron table ? Est-ce que je dois créer un objet métier ?

Merci d’avance.

Bonjour

  1. pour créer une tache planifiée
  • il suffit de créer une Action de liste back-office (avec une méthode) sur un objet métier (dédié ou existant typiquement l’objet source du flux sortant, hidden si non accessible par UI).
  • L’habiliter au user qui lancera la tache cron
  • Parametrer la crontab en précisant quel user lance la tache : à defaut c’est designer qui devra avoir tous les droits sur l’action ou les objets utilisés, ou ajouter des changeAccess dynamiques dans votre code
  1. coder l’action
    La méthode publique de l’objet fait ce que vous voulez en java / javascript.
    Nous conseillons désormais de tout faire en java.
  • select SQL direct ou object.getCurrentList ou search pour chercher vos données
  • padding de vos champ avec des espaces
  • output stream pour écrire dans un ficher (vers grant.getExportDir() ou via path d’un paramètre système pour que ce soit paramétrable)
    Rien de spécifique à Simplicité à ce niveau.

Bonjour,

Je n’ai pas d’objet métier dédié pour ce dataset d’export (c’est un dataset particulier qui ne correspond pas à un cas fonctionnel autre).
Je crée donc un objet métier vide (sans champs) qui portera le code de mon export ? Est-ce qu’il faut que je paramètre quoi que ce soit d’autre sur l’objet métier ou je n’y crée que mon code d’action ?

Ok pour les autres points (Action, etc.), merci.

Un objet métier sans champ n’a pas de sens (il faut une clé fonctionnelle, des fonctions CRUD…).
Les données exportées viennent nécessairement d’au moins un de vos objets métier.
Mettez y la méthode callback qui peut si vous préférez uniquement appeler un code partagé dédié.

Ok, merci. Je mettrai donc un timestamp de lancement du script comme clé fonctionnelle.

Bonjour @Francois ;

Nous avons essayé de générer un fichier via action avec le processus suivant mais rien n’est généré, il manque sans doute une étape :

  1. Création d’un objet métier “SitesGenerateRaftAD” qui contient la méthode generateAD avec le code suivant :
	public void generateRaftAD() throws IOException {
        PrintWriter writer1 = new PrintWriter(getGrant().getExportDir() + "/RaftADFile.txt", "UTF-8");
		writer1.append("Test");
        PrintWriter writer2 = new PrintWriter(getGrant().getExportDir() + "RaftADFile.txt", "UTF-8");
		writer2.append("Test");
		writer1.close();
		writer2.close();
	}

Nous avons fait deux writer, un avec un slash et l’autre sans car nous n’avons pas pu trouver de doc Simplicité qui explique le format renvoyé par getExportDir().

  1. Créé une action de type List, exécution Back, asynchrone et méthode generateRaftAD. Puis une fonction sur cette action qui est liée à l’objet métier SitesGenerateRaftAD.

  2. On voit bien le bouton d’action GenerateRaftAD sur notre objet métier et quand on l’exécute il log une exécution :

019-11-08 12:02:09,880 INFO [com.simplicite.util.CronJob] SIMPLICITE|http://9ff6ede8d4ca:8080||ICORECM005|system|com.simplicite.util.CronJob|run||Result of job SitesGenerateRaftAD-SitesLaunchRaftAD :
2019-11-08 12:02:09,879 INFO [com.simplicite.util.CronJob] SIMPLICITE|http://9ff6ede8d4ca:8080||ICORECM004|system|com.simplicite.util.CronJob|run||Execute job SitesGenerateRaftAD-SitesLaunchRaftAD at 2019-11-08 12:02:09

En revanche, en cherchant via recherche globale on ne retrouve pas de fichier RaftADFile.txt. On a également voulu rechercher dans “File search” dans le menu “Document” mais en ouvrant cette page, le message “No bookshelf” est affiché. Est-ce qu’il faut créer une bookshelf pour créer un fichier ? Est-ce qu’il faut faire quelque chose d’autre ?

Merci d’avance.

Bonjour,

Une méthode d’action back doit retourner un String et pas d’exception.
J’imagine que votre méthode n’est pas trouvée par le runtime puisque n’a pas la bonne signature.

public String generateRaftAD()  {
   try {
      Grant g = getGrant();
      AppLog.info(getClass(), "generateRaftAD", "export dir="+g.getExportDir(), g);
      // ...
      return Message.formatSimpleInfo("Terminé OK");
   }
   catch(Exception e) {
      AppLog.error(getClass(), "generateRaftAD", "erreur...", e, g);
      return Message.formatSimpleError("Erreur...");
  }
}

Il est aussi possible de récupérer les paramètres de confirmation si votre action possède des champs.
public String generateRaftAD(Map<String,String> params)

Pour voir ce que vaut getGrant().getExportDir(), comme toute autre information, il suffit d’ajouter une trace via AppLog.info et de regarder la console ou les logs.
Normalement ça pointe vers le dbdoc/export en chemin absolu, et il faut bien ajouter un /.

Bonjour,

Voici notre code maintenant :

	public String generateRaftAD() {
		Grant g = getGrant();
		try {
		    AppLog.info(getClass(), "generateRaftAD", "export dir=" + g.getExportDir(), g);
	        PrintWriter writer = new PrintWriter(getGrant().getExportDir() + "/RaftADFile.txt", "UTF-8");
			writer.append("Test generateRaftAD");
			writer.close();
    		return Message.formatSimpleInfo("Finished OK");
		}	
		catch(Exception e) {
		    AppLog.error(getClass(), "generateRaftAD", "error...", e, g);
		    return Message.formatSimpleError("Error...");
		}
	}

L’export pointe vers:
dir=/usr/local/tomcat/webapps/ROOT/WEB-INF/dbdoc/export

Est ce que le dbdoc est lié à quelques chose dans simplicité pour qu’on puisse y accéder et récupérer le fichier?

J’imagine qu’on parle de déploiements en containers Docker ?

Si oui écrire dans une répertoire éphémère du container est une mauvaise approche (à moins de l’avoir monté en volume persistant)

Il faut soit stocker le fichier dans un stotrage externe (type S3) soit le mettre dans un attribut de tye document d’un objet métier ad hoc (le fichier sera alors stocke en base de données et accessible via les APIs standard de la plateforme)

Bonjour,

En effet, c’est un déploiement en containers Docker. Nous comptons stocker le fichier sur un S3 à terme mais sommes dans l’attente de la mise en place de cette solution. Afin de tester le développement de notre méthode d’export de fichier, nous voulons pouvoir ouvrir le fichier dans notre contexte actuel le temps du développement.

La deuxième solution semble correspondre dans ce cas là (qui sera donc temporaire). Y a-t-il une documentation ou un exemple détaillant comment mettre ceci en place ? (sauvegarder le fichier dans un attribut de type document d’un objet métier ad hoc)

Merci d’avance.

Cf. https://docs.simplicite.io/documentation/01-core/documents-code-examples.md

Bonjour,
On vient d’ajouter un field de type document (sitesAdfAdFile) à notre business object. Voici notre code maintenant:

public String generateRaftAD() {
		Grant g = getGrant();
		ByteArrayOutputStream baos = null;
		try {
		    AppLog.info(getClass(), "generateRaftAD", "export dir=" + g.getExportDir(), g);
			baos = new ByteArrayOutputStream();
			String content = "Test generateRaftAD";
			baos.write(content.getBytes());
			this.getField("sitesAdfAdFile").setDocument(this, "RaftADFile.pdf", baos.toByteArray());
			this.save();
    		return Message.formatSimpleInfo("Finished OK");
		}	
		catch(Exception e) {
		    AppLog.error(getClass(), "generateRaftAD", "error...", e, g);
		    return Message.formatSimpleError("Error...");
		}
	}

Cepandant, on arrive pas à trouver le fichier.

Merci de votre réponse

Vous ne gérez pas les éventuelles erreur d’enregistrement. Donc si ça se trouve le save() est en erreur… D’autant qu’en outre vous ne passez pas par la validation des données.

Dans une logique de try/catch il faut écrire plutôt:

new BusinessObjectTool(this).validateAndSave();

Cf. https://docs.simplicite.io/4.0/javadoc/com/simplicite/util/tools/BusinessObjectTool.html

Par ailleurs je n’ai pas compris si vous vouliez faire une mise à jour du record courant de votre objet ou créer un nouveau record (auquel cas c’est un validateAndCreate précédé d’un getFoCreate qu’il faut utiliser). Votre action est une action globale (liste) ou individuelle (form) ?

Bonjour,

Nous avons créé une action globale (liste) mais ce choix a été fait arbitrairement. Nous ne savons pas quel type d’action est censé être choisi pour faire un export de fichier paramétré via crontab.

Notre code actuel après modifications suite aux derniers messages :

            public String generateRaftAD() {
                        Grant g = getGrant();
                        ByteArrayOutputStream baos = null;
                        SitesGenerateRaftAD adf = (SitesGenerateRaftAD) .getTmpObject("SitesGenerateRaftAD");
                        BusinessObjectTool adfTool = new BusinessObjectTool(adf);
                        try {
                                    baos = new ByteArrayOutputStream();
                                    String content = "Test generateRaftAD";
                                    baos.write(content.getBytes());
                                    adfTool.getForCreate();
                                    adf.getField("sitesAdfAdFile").setDocument(adf, "RaftADFile.pdf", baos.toByteArray());
                                    adfTool.validateAndCreate();
                     return Message.formatSimpleInfo("Finished OK");
                        }          
                        catch(Exception e) {
                            AppLog.error(getClass(), "generateRaftAD", "error...", e, g);
                            return Message.formatSimpleError("Error...");
                        }
            }

Mais en lançant l’action, on a l’erreur suivante :

2019-11-14 11:25:23,801 ERROR [com.simplicite.objects.Application.SitesGenerateRaftAD] SIMPLICITE|http://7ef016ba3dc1:8080||ERROR|designer|com.simplicite.objects.Application.SitesGenerateRaftAD|generateRaftAD||Event: error...
    com.simplicite.util.exceptions.CreateException: ERR_GRANT
     at com.simplicite.util.tools.BusinessObjectTool.create(BusinessObjectTool.java:293)
     at com.simplicite.util.tools.BusinessObjectTool.validateAndCreate(BusinessObjectTool.java:344)
     at com.simplicite.objects.Application.SitesGenerateRaftAD.generateRaftAD(SitesGenerateRaftAD.java:34)
     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
     at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
     at java.base/java.lang.reflect.Method.invoke(Method.java:567)
     at com.simplicite.util.engine.ObjectManager.invokeActionSync(ObjectManager.java:3487)
     at com.simplicite.util.ObjectDirect.invokeAction(ObjectDirect.java:672)
     at com.simplicite.util.ObjectDB.invokeAction(ObjectDB.java:1697)
     at com.simplicite.util.CronJob.run(CronJob.java:356)

Est-ce que la manière dont on a récupéré le grant n’est pas bonne ?

Une action destinée à être lancée par tâche planifiée ne peut pas être une action d’item (car comment savoir de quel item on parle ?), donc une action globale c’est le bon choix.

S’agissant des droits si vous écrivez getGrant() vous utilisez les droits du user qui lance l’action (donc celui indiqué sur la tache planifiée dans ce cas là, ou sinon celui qui clique sur le bouton dans la UI, ou celui qui invoque cette action via API).

Celui-ci doit donc avoir les droits suffisants pour ce qu’on lui fait faire dans l’action. Ici clairement ce user n’a pas les droits de création sur l’objet SitesGenerateRaftAD

Ok, ça marche maintenant, merci. Si on tente de générer un fichier .pdf comme dans l’exemple de la doc, il est corrompu et on ne peut pas l’ouvrir, mais avec un fichier .txt (ça nous convient) ça fonctionne correctement.

OK
Copie/colle moi le code qui génère du PDF et qui ne marche pas pour que je teste.

C’est le code précédent, je n’ai changé que l’extension pour avoir un export en .txt qui fonctionne.

Il vient du premier exemple de la doc : https://docs.simplicite.io/documentation/01-core/documents-code-examples.md

Ben pour générer un PDF c’est pas juste une question d’extension de fichier…

Pour générer un PDF il faut utiliser la lib iText (via notre wrapper simplifié PDFTool ou directement) ou la lib PDFBox.

Exemple avec iText:

import com.lowagie.text.*;
(...)
ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
Document pdf = PDFTool.open(bos);
pdf.add(new Paragraph("Hello world !"));
(...)
PDFTool.close(pdf);
byte[] bytes = bos.toByteArray();

cf. https://docs.simplicite.io/documentation/01-core/documents-code-examples.md#pdf

D’accord, c’est bon à savoir. Merci !