BOM signifie Byte Order Mark
👉 en français : marque d’ordre des octets

C’est une séquence d’octets placée au tout début d’un fichier texte pour indiquer :

l’encodage du fichier

parfois l’ordre des octets (endianness)

📌 À quoi sert un BOM ?
1️⃣ Identifier l’encodage

Quand un logiciel ouvre un fichier, le BOM lui dit :

« Ce fichier est en UTF-8 / UTF-16 / UTF-32 »

Sans BOM, certains outils devinent l’encodage… et se trompent.

2️⃣ Indiquer l’ordre des octets (UTF-16 / UTF-32)

Pour les encodages multi-octets :

Little Endian

Big Endian

Le BOM permet de savoir dans quel ordre lire les octets.

Sur L’IBMi on l’utilisera pour échanger un fichier UTF8 avec un autre système que le réclame, ou à l’inverse il faudra l’enlever sur un fichier recu !

Voici 2 procédures pour vous aider et une requête

Une requête pour afficher le BOM sur un fichier IFS

WITH FICHIER AS (
    SELECT DATA
    FROM TABLE (
        QSYS2.IFS_READ_BINARY('/tmp/test.txt')
    )
)
SELECT
    HEX(SUBSTR(DATA, 1, 4))      AS BOM_HEXA,
    CASE
        WHEN SUBSTR(DATA, 1, 3) = X'EFBBBF'
            THEN 'UTF-8 avec BOM'
        WHEN SUBSTR(DATA, 1, 2) = X'FFFE'
            THEN 'UTF-16 Little Endian'
        WHEN SUBSTR(DATA, 1, 2) = X'FEFF'
            THEN 'UTF-16 Big Endian'
        WHEN SUBSTR(DATA, 1, 4) = X'FFFE0000'
            THEN 'UTF-32 Little Endian'
        WHEN SUBSTR(DATA, 1, 4) = X'0000FEFF'
            THEN 'UTF-32 Big Endian'
        ELSE
            'Aucun BOM détecté (UTF-8 sans BOM probable)'
    END AS ENCODAGE_DETECTE
FROM FICHIER;

Une procédure pour ajouter un BOM sur un fichier à envoyer

CREATE OR REPLACE PROCEDURE AJOUTER_BOM (
IN p_path VARCHAR(1024) CCSID 1208,
IN p_bom VARCHAR(10)
)
LANGUAGE SQL
BEGIN
DECLARE v_data BLOB(10M);
DECLARE v_new_data BLOB(10M);
DECLARE v_bom BLOB(4);-- Déterminer le BOM à ajouter
SET v_bom =
    CASE UPPER(p_bom)
        WHEN 'UTF8'     THEN X'EFBBBF'
        WHEN 'UTF16LE'  THEN X'FFFE'
        WHEN 'UTF16BE'  THEN X'FEFF'
        ELSE NULL
    END;

IF v_bom IS NULL THEN
    SIGNAL SQLSTATE '38001'
        SET MESSAGE_TEXT = 'BOM non reconnu (UTF8, UTF16LE, UTF16BE)';
END IF;

-- Lire le fichier
SELECT DATA
  INTO v_data
  FROM TABLE (
    QSYS2.IFS_READ_BINARY(p_path)
  );

-- Vérifier si le BOM est déjà présent
IF SUBSTR(v_data, 1, LENGTH(v_bom)) <> v_bom THEN
    SET v_new_data = v_bom || v_data;

    -- Réécriture du fichier avec BOM
    CALL QSYS2.IFS_WRITE_BINARY(
        PATH_NAME => p_path,
        DATA      => v_new_data,
        REPLACE   => 1
    );
END IF;
END;

Une procédure pour enlever le BOM

CREATE OR REPLACE PROCEDURE SUPPRIMER_BOM (
IN p_path VARCHAR(1024) CCSID 1208,
IN p_bom VARCHAR(10)
)
LANGUAGE SQL
BEGIN
DECLARE v_data BLOB(10M);
DECLARE v_bom BLOB(4);
DECLARE v_len INTEGER;-- Déterminer le BOM à supprimer
SET v_bom =
    CASE UPPER(p_bom)
        WHEN 'UTF8'     THEN X'EFBBBF'
        WHEN 'UTF16LE'  THEN X'FFFE'
        WHEN 'UTF16BE'  THEN X'FEFF'
        WHEN 'UTF32LE'  THEN X'FFFE0000'
        WHEN 'UTF32BE'  THEN X'0000FEFF'
        ELSE NULL
    END;

IF v_bom IS NULL THEN
    SIGNAL SQLSTATE '38001'
        SET MESSAGE_TEXT =
          'BOM non reconnu (UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE)';
END IF;

SET v_len = LENGTH(v_bom);

-- Lire le fichier
SELECT DATA
  INTO v_data
  FROM TABLE (
    QSYS2.IFS_READ_BINARY(p_path)
  );

-- Vérifier et supprimer le BOM demandé
IF SUBSTR(v_data, 1, v_len) = v_bom THEN
    SET v_data = SUBSTR(v_data, v_len + 1);

    -- Réécrire le fichier sans BOM
    CALL QSYS2.IFS_WRITE_BINARY(
        PATH_NAME => p_path,
        DATA      => v_data,
        REPLACE   => 1
    );
END IF;
END;

Conclusion

Sur IBMi ca ne sert à rien le CCSID donne cette information

Pour en savoir plus Indicateur d’ordre des octets — Wikipédia

En novembre 2025, IBM a fait évoluer IWS de la version 2.6 à la version 3.0

Outre le passage à Jakarta EE au lieu de Java EE, la principale nouveauté concerne l’intégration de d’open api.

Création d’une instance

La version de l’instance est affichée :

Open api

Une fois l’instance créée, vous avez désormais accès à l’interface open api :

http[s]://instance:port/openapi/ui

L’interface présente l’ensemble des services déployés dans le serveur, avec les routes (opérations).

Vous avez également la possibilité de tester les services en direct :

L’outil affiche les syntaxes curl correspondantes !

Evidemment, seuls les services REST sont affichés dans l’interface openapi.

Vous pouvez personnaliser certains attributs openapi via la commande setWebServicesServerProperties et un fichier de configuration à fournir.

Migration des instances

Toutjours par script shell (dans /QIBM/ProdData/OS/WebServices/bin), vous pouvez migrer vos instances IWS 2.6 en 3.0 :

updateWebServicesServer.sh -server instance -version *CURRENT

Lors de l’opération :

  • Les serveur est migré en 3.0
  • Les services sont convertis

en cas d’erreur, vous pouvez lancer la conversion de services par updateWebServices.sh.

Dans nos différents tests, nous n’avons pas observé de modification de comportement des services migrés.

Par contre, nous observons, pour l’instant, des erreurs de conversion pour les services SOAP.

Voir la documentation : https://www.ibm.com/support/pages/node/7248102

, , Comprendre l’impact du CCSID de votre source

Vous avez vos sources coté IBMi qui sont le plus souvent stockés dans des fichiers sources QRPGLESRC et le CCSID est à 1147 (je reviens pas sur les dangers du 65635)

Vous décidez de placer, vos sources dans l’IFS et vous décidez d’adopter un (CCSID UTF-8 recommandé) ,voir Windows 1252.

C’est le sens de l’histoire pour giter par exemple

Le CCSID du source a un impact sur les constantes de votre programme RPGLE, à la compilation on va voir un exemple :

Les constantes étant basées sur le CCSID du source par défaut

Voici un programme

**free                                                                 
ctl-opt dftactgrp(*no)                                                 
  //    ccsid(*exact)    // << clé : figer au CCSID de                   
  ;                                                                    
dcl-c titre 'Tarif semaine dès 4 jours ?  ' ;   // Constante           
dcl-s mots char(06) dim(3) ctdata perrcd(1);    // tableau chargé à la 
dcl-ds ds_texte    ;                            // tableau de constante
      *N Char(06) Inz('école' ) ;       //                             
      *N Char(06) Inz('garçon') ;       //                             
      *N Char(06) Inz('café ' ) ;       //                             
      texte   char(06) dim(3) pos(1) ;                                 
end-ds;                                                                
dcl-s i     int(10);                                                   
// Affichage Résultat                                                  
dsply ('Constante : ' + titre);                                        
for i = 1 to %elem(texte) ;                                            
  dsply ('Texte    : ' + texte(i));                                    
endfor; 
for i = 1 to %elem(mots);                     
  dsply (' CTDATA   : ' + mots(i));           
endfor;                                       
*inlr = *on;                                  
** CTDATA mots (3 lignes, codées avec accents)
école                                         
garçon                                        
café

Coté IBMi

Avec un ccsid 1147

CRTBNDRPG PGM(GDATA/SRCCCSID)
SRCFILE(GDATA/QRPGLESRC)

Vous obtenez ceci

Coté IFS

Avec un ccsid UTF-8

CRTBNDRPG PGM(GDATA/SRCCCSID)      

          SRCSTMF(‘SRCCCSID.RPGLE’)

Vous obtenez ceci

Vous perdez les caractères spéciaux

Vous avez une option de compile TGTCCSID qui fixe le CCSID à utiliser

donc on ajoute l’option

CRTBNDRPG PGM(GDATA/SRCCCSID)      

          SRCSTMF(‘SRCCCSID.RPGLE’)

          TGTCCSID(1147)    

L’affichage est correct

Conclusion :

Vous pouvez également utiliser *JOB dans la valeur de TGTCCSID(), à ce moment la vous aurez le même comportement dans les 2 cas si votre job est en 1147.

On expliquera ccsid(*exact) dans un autre post

t