Technology Blog Posts by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
Vi_Ma
Product and Topic Expert
Product and Topic Expert
2,074

Introduction

This blog focuses on how to get documents out from the SAP Print Queue and how to pass them on to an external Output Management System (OMS) for processing and printing using the SAP provided API. It supplements the already existing blog with example implementation with JAVA code of  SAP_COM_0466 - Print Queue Item - Read (A2X).

Example code for SAP_COM_0466 - Print Queue Item - Read (A2X)

See the details on SAP_COM_0466 | SAP Help Portal how to configure your SAP S/4HANA Cloud Public Edition system to support Print Queues. 
Prerequisites: 

  • Compiled with SAPMachine 21. You can download here.
  • Download (e.g. here) and add latest version of class org.json 
  • Adapt with your personal connection and access details.  

Please note that code is meant as example how the api can be used. Code is not claimed to be complete, to be error free or to be state of the art programming style. 

Usage of the example code

  1. Download (e.g. here) and add latest version of class org.json 
  2. Adopt cope with your access details
  3. Compile code (I used SAPMachine 21).
  4. Run code
  5. Create a document on the configured print queue in SAP System
  6. Code will  process print queue item
  7. You can also simulate errors on the print queue item. See comments in the code

Remind!
Code is meant as example how the api can be used. Code is not claimed to be complete, to be error free or to be state of the art programming style. Nevertheless feedback is welcome.

import java.net.CookieManager;
import java.net.http.*;
import java.net.URI;
import java.util.Base64;
import java.util.concurrent.*; 
import org.json.*;

class SAP_COM_0466_example {
    
    public static int Get_System_Details(  System_Str[] System_Details ) throws Exception {
    // get details of the SAP sytem for api and other calls
    
        // pre-defined system details and credentials for testing, needs to be configured in SAP system beforehand
        
		System_Details[0] = new System_Str (
            "https://my######-api.s4hana.ondemand.com",                                                        // URL to SAP system DEF
            "SAPUSERNAME",                                                                                     // SAP User Name / Client ID on BTP
            "########################################",                                                        // SAP User Password / Client Secret on BTP
            "SAP S/4HANA CLOUD (Public Edition)",                                                              // SAP System Type
            "dummy",                                                                                           // Sub-Account name, only required for BTP 
            10);                                                                                               // intervall for ICM cache polling

        /*                                                                                       
        System_Details[0] = new System_Str (                                                 
            "https://abc.private.corp",                                                                        // URL to SAP system ABC
            "SAPUSERNAME",                                                                                     // SAP User Name / Client ID on BTP
            "########################################",                                                        // SAP User Password / Client Secret on BTP
            "SAP S/4HANA CLOUD (Private Edition)",                                                             // SAP System Type
            "dummy",                                                                                           // Sub-Account name, only required for BTP 
            10);                                                                                               // intervall for ICM cache polling
        */                                                                                         

        /*                                                                                       
        System_Details[0] = new System_Str (                                                 
            "https://api.eu11.print.services.sap/qm",                                                          // URL to SAP system BTP
            "#############################################################",                                   // SAP User Name / Client ID on BTP
            "#################################################################################",               // SAP User Password / Client Secret on BTP
            "SAP BTP",                                                                                         // SAP System Type
            "icc-btp-test-abcd1234");                                                                          // Sub-Account name, only required for BTP
            10);                                                                                               // intervall for ICM cache polling
        */
     
        return (200);                                                                                          // return good status code
     
    }


    public static int Get_Bearer_Token( StringBuilder BearerToken, String Credentials, System_Str System_Details ) throws Exception {
    // For BTP only to get the bearer token for oauth authentication 

        String       UrlLocator;                                                                               // http request url
        HttpRequest  RequestBody;                                                                              // http request body
        HttpHeaders  RequestHeaders;                                                                           // http request header
        HttpResponse ResponseReply;                                                                            // http reply body
        HttpHeaders  ResponseHeaders;                                                                          // http reply header
        String       ResponseCodeL;                                                                            // http response code
        String       BearerTokenL;                                                                             // BTP bearer token for oauth authentication
        
        UrlLocator = System_Details.SystemUrl.substring(System_Details.SystemUrl.indexOf("api.") + 4);         // extract location of BTP account
        UrlLocator = UrlLocator.substring(0,UrlLocator.indexOf("."));                                 
        UrlLocator = "https://" + System_Details.SubDomain + ".authentication." + UrlLocator + ".hana.ondemand.com"; // build url for bearer token request
        //System.out.println(UrlLocator + "/oauth/token?grant_type=client_credentials");              
                                                                                                      
        RequestBody = HttpRequest.newBuilder()                                                                 // build the http request
        .uri(URI.create( UrlLocator + "/oauth/token?grant_type=client_credentials"))                           // add path for bearer token request
        .version(HttpClient.Version.HTTP_2)                                                                    // set call context
        .header("Content-Type", "application/x-www-form-urlencoded")
        .header("accept", "application/xml")
        .header("user-agent", "printqueuequery")
        .header("Authorization", Credentials)
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        RequestHeaders  = RequestBody.headers();                                                               // read the request header for error analysis 
        ResponseReply   = HttpClient.newHttpClient().send(RequestBody, HttpResponse.BodyHandlers.ofString());  // initiate http call
        ResponseHeaders = ResponseReply.headers();                                                             // read the reply header for error analysis    
        ResponseCodeL   = ResponseReply.toString();                                                            
        ResponseCodeL   = ResponseCodeL.substring(ResponseCodeL.length()-3,ResponseCodeL.length());            // extract http status code

        //System.out.println("");
        //System.out.println("Get_Bearer_Token");
        //System.out.println("");
        //System.out.println("HttpRequest");    
        //System.out.println(RequestBody);        
        //System.out.println("");
        //System.out.println("RequestHeaders");
        //System.out.println(RequestHeaders);
        //System.out.println("");
        //System.out.println("ResponseReply");    
        //System.out.println(ResponseReply);    
        //System.out.println("");
        //System.out.println("ResponseBody");    
        //System.out.println(ResponseReply.body());    
        //System.out.println("");
        //System.out.println("ResponseHeaders");
        //System.out.println(ResponseHeaders);
        //System.out.println("");        
        //System.out.println("BearerToken: \"" + BearerTokenL + "\"");        
        //System.out.println("");
                    
        BearerTokenL    = ResponseReply.body().toString();                                                     // extract bearer token from http reply
        if ( BearerTokenL.contains("\"access_token\":\"") )                                                    // check if bearer token is in http reply
        {
            BearerTokenL    = BearerTokenL.substring(BearerTokenL.indexOf("\"access_token\":\"")+16);          // extract bearer token
            BearerTokenL    = BearerTokenL.substring(0,BearerTokenL.indexOf("\","));
        }
        else {
            BearerTokenL    = "";
            System.out.println("    BearerToken missing in http-reply");                                       // show error message if bearer token is missing
        }

        BearerToken.setLength(0);                                                                              // return the bearer token
        BearerToken.append(BearerTokenL);
        
        return (Integer.valueOf(ResponseCodeL));                                                               // return the http status code
    }        
  
  
    public static int Get_IcmCache( StringBuilder ICMcache, String Credentials , System_Str System_Details) throws Exception {
    // see SAP doumenation for the API

        HttpRequest  RequestBody;                                                                              // http request body
        HttpHeaders  RequestHeaders;                                                                           // http request header
        HttpResponse ResponseReply;                                                                            // http reply body
        HttpHeaders  ResponseHeaders;                                                                          // http reply header
        String       ResponseCodeL;                                                                            // http response code
        String       ICMCacheL;                                                                                // SAP ICM cache status
                                                                                                             
        RequestBody = HttpRequest.newBuilder()                                                                 // build the http request
        .uri(URI.create(System_Details.SystemUrl + "/sap/opu/odata/SAP/API_CLOUD_PRINT_PULL_SRV/Get_IcmCache"))  // add path for function
        .version(HttpClient.Version.HTTP_2)                                                                    // set call context
        .header("Content-Type", "application/json")
        .header("accept", "application/json")
        .header("user-agent", "printqueuequery")
        .header("Authorization", Credentials)
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        RequestHeaders  = RequestBody.headers();                                                               // read the request header for error analysis 
        ResponseReply   = HttpClient.newHttpClient().send(RequestBody, HttpResponse.BodyHandlers.ofString());  // initiate http call
        ResponseHeaders = ResponseReply.headers();                                                             // read the reply header for error analysis    
        ResponseCodeL   = ResponseReply.toString();                                                
        ResponseCodeL   = ResponseCodeL.substring(ResponseCodeL.length()-3,ResponseCodeL.length());            // extract http status code
        
        //System.out.println("");
        //System.out.println("Get_IcmCache");
        //System.out.println("");
        //System.out.println("HttpRequest");    
        //System.out.println(RequestBody);        
        //System.out.println("");
        //System.out.println("RequestHeaders");
        //System.out.println(RequestHeaders);
        //System.out.println("");
        //System.out.println("ResponseReply");    
        //System.out.println(ResponseReply);    
        //System.out.println("");
        //System.out.println("ResponseBody");    
        //System.out.println(ResponseReply.body());    
        //System.out.println("");
        //System.out.println("ResponseHeaders");
        //System.out.println(ResponseHeaders);
        //System.out.println("");
    
        ICMCacheL = "";                                                                                        // extract the SAP ICM cache status from http reply
        if ( ResponseReply.body().toString().length() > 99 ) {                                                 // check if SAP ICM cache status is in  http reply
            if ( !ResponseReply.body().toString().contains("{\"d\":{") ) {                                     // check if proper structure is received
				System.out.println("error occured");                                                           // exit in case of elemd "d" does not exist
				System.out.println(ResponseReply.body());    
				System.exit(0);
			}
            JSONObject jObject = new JSONObject(ResponseReply.body().toString());                              // read the json structure
			ICMCacheL = jObject.getJSONObject("d").getString("content");                                       // extract the SAP ICM cache status				
        }
        ICMcache.setLength(0);                                                                                 // return the SAP ICM cache status
        ICMcache.append(ICMCacheL);

        return (Integer.valueOf(ResponseCodeL));                                                               // return the http status code
    }    

    
    public static int Get_PrintQueuesOfUserExt( int[] Qname_No, String[] ListOfQueues, String Credentials, System_Str System_Details ) throws Exception {
    // see SAP doumenation for the API
    
        HttpRequest  RequestBody;                                                                              // http request body
        HttpHeaders  RequestHeaders;                                                                           // http request header
        HttpResponse ResponseReply;                                                                            // http reply body
        HttpHeaders  ResponseHeaders;                                                                          // http reply header
        String       ResponseCodeL;                                                                            // http response code
        int          Qname_NoL;                                                                                // Number of Print Queues

        RequestBody = HttpRequest.newBuilder()                                                                 // build the http request
        .uri(URI.create(System_Details.SystemUrl + "/sap/opu/odata/SAP/API_CLOUD_PRINT_PULL_SRV/Get_PrintQueuesOfUserExt"))   // add path for function
        .version(HttpClient.Version.HTTP_2)                                                                    // set call context
        .header("Content-Type", "application/json")
        .header("accept", "application/json")
        .header("user-agent", "printqueuequery")
        .header("Authorization", Credentials)
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        RequestHeaders  = RequestBody.headers();                                                               // read the request header for error analysis 
        ResponseReply   = HttpClient.newHttpClient().send(RequestBody, HttpResponse.BodyHandlers.ofString());  // initiate http call
        ResponseHeaders = ResponseReply.headers();                                                             // read the reply header for error analysis   
        ResponseCodeL   = ResponseReply.toString();                                                
        ResponseCodeL   = ResponseCodeL.substring(ResponseCodeL.length()-3,ResponseCodeL.length());            // extract http status code            

        //System.out.println("");
        //System.out.println("Get_PrintQueuesOfUserExt");
        //System.out.println("");
        //System.out.println("HttpRequest");    
        //System.out.println(RequestBody);        
        //System.out.println("");
        //System.out.println("RequestHeaders");
        //System.out.println(RequestHeaders);
        //System.out.println("");
        //System.out.println("ResponseReply");    
        //System.out.println(ResponseReply);    
        //System.out.println("");
        //System.out.println("ResponseBody");    
        //System.out.println(ResponseReply.body());    
        //System.out.println("");
        //System.out.println("ResponseHeaders");
        //System.out.println(ResponseHeaders);
        //System.out.println("");        
        
        Qname_NoL = 0;                                                                                         // initiate the number of Print Queues
        if ( ResponseCodeL.equals("200") && (ResponseReply.body().toString().length() > 99) ) {               // check if there is payload
            if ( !ResponseReply.body().toString().contains("{\"d\":{") ) {                                     // check if proper structure is received
				System.out.println("error occured");                                                           // exit in case of elemd "d" does not exist
				System.out.println(ResponseReply.body());    
				System.exit(0);
			}
            JSONObject jObject = new JSONObject(ResponseReply.body().toString());                              // read the json structure
            JSONArray  Qresults = jObject.getJSONObject("d").getJSONArray("results"); 
            for (int i = 0; i < Qresults.length(); i++) {
                ListOfQueues[i]  = Qresults.getJSONObject(i).getString("Qname");                               // read the Print Queue name
                //System.out.println(ListOfQueues[i]);                                                                 
                Qname_NoL = i + 1;                                                                             // adjust number of Print Queues
                if ( Qname_NoL > 100 ) { 
                    System.out.println("Support max. 100 print queues");                                       // inform on limitation                          
                    break;                                                                                     // stop after 100 Print Queues 
                }   
            }
        }
        Qname_No[0] = Qname_NoL;                                                                               // return the number of Print Queues 
        
        return (Integer.valueOf(ResponseCodeL));                                                               // return the http status code
    }        

    
    public static int Get_NumberOfQItemsExt( int[] NrOfNewItems, String Qname, String Credentials, System_Str System_Details ) throws Exception {
    // see SAP doumenation for the API
    
        HttpRequest  RequestBody;                                                                              // http request body 
        HttpResponse ResponseReply;                                                                            // http request header
        HttpHeaders  RequestHeaders;                                                                           // http reply body
        HttpHeaders  ResponseHeaders;                                                                          // http reply header
        String       ResponseCodeL;                                                                            // http response code
        String       paramset;                                                                                 // parameter set for api call
        int          NrOfNewItemsL;                                                                            // number of new print queue items
                                                                                                               
        paramset = "?Qname='" + Qname + "'";                                                                   // build the parameter set - set print queue name 
        RequestBody = HttpRequest.newBuilder()                                                                 // build the http request
        .uri(URI.create(System_Details.SystemUrl + "/sap/opu/odata/SAP/API_CLOUD_PRINT_PULL_SRV/Get_NumberOfQItemsExt" + paramset)) // add path for function
        .version(HttpClient.Version.HTTP_2)                                                                    // set call context
        .header("Content-Type", "application/json")                                                         
        .header("accept", "application/json")                                                               
        .header("user-agent", "printqueuequery")
        .header("Authorization", Credentials)
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        RequestHeaders  = RequestBody.headers();                                                               // read the request header for error analysis 
        ResponseReply   = HttpClient.newHttpClient().send(RequestBody, HttpResponse.BodyHandlers.ofString());  // initiate http call
        ResponseHeaders = ResponseReply.headers();                                                             // read the reply header for error analysis   
        ResponseCodeL   = ResponseReply.toString();                                                
        ResponseCodeL   = ResponseCodeL.substring(ResponseCodeL.length()-3,ResponseCodeL.length());            // extract http status code            
                                                                                                               
        //System.out.println("");
        //System.out.println("Get_NumberOfQItemsExt");
        //System.out.println("");
        //System.out.println("HttpRequest");    
        //System.out.println(RequestBody);        
        //System.out.println("");
        //System.out.println("RequestHeaders");
        //System.out.println(RequestHeaders);
        //System.out.println("");
        //System.out.println("ResponseReply");    
        //System.out.println(ResponseReply);    
        //System.out.println("");
        //System.out.println("ResponseBody");    
        //System.out.println(ResponseReply.body());    
        //System.out.println("");
        //System.out.println("ResponseHeaders");
        //System.out.println(ResponseHeaders);
        //System.out.println("");
        //System.out.println("NrOfNewItems: " + NrOfNewItemsL);
        //System.out.println("");

        NrOfNewItemsL = 0;                                                                                     // initiate the number of print queue items
        if ( ResponseReply.body().toString().length() > 99 ) {                                                 // check if there is payload
            if ( !ResponseReply.body().toString().contains("{\"d\":{") ) {                                     // check if proper structure is received
				System.out.println("error occured");                                                           // exit in case of elemd "d" does not exist
				System.out.println(ResponseReply.body());    
				System.exit(0);
			}
            JSONObject jObject = new JSONObject(ResponseReply.body().toString());                              // read the json structure
            NrOfNewItemsL  = jObject.getJSONObject("d").getInt("NrOfNewItems");                                // read from json structure number of print queue items 
        }
        NrOfNewItems[0] = NrOfNewItemsL;                                                                       // return the number of print queue items 
        
        return (Integer.valueOf(ResponseCodeL));                                                               // return the http status code
    }


    public static int Get_Next_QItemsExt( QItem_Str[] QItemList, int NumberOfItems, String Qname, String Credentials, System_Str System_Details ) throws Exception {
    // see SAP doumenation for the API
    
        HttpClient   client;                                                                                   // http handle 
        HttpRequest  RequestBody;                                                                              // http request body 
        HttpResponse ResponseReply;                                                                            // http request header
        HttpHeaders  RequestHeaders;                                                                           // http reply body
        HttpHeaders  ResponseHeaders;                                                                          // http reply header
        String       ResponseCodeL;                                                                            // http response code
        String       QItemDoc;                                                                                 // queue item docuement raw data
        String       QItemId;                                                                                  // queue item ID
        String       QItemFile;                                                                                // queue item file name
        String       QItemBlob;                                                                                // queue item blob
        long         QItemSize;                                                                                // queue item file size
        String       paramset;                                                                                 // parameter set for api call
        
        paramset = "?NumberOfItems=" + NumberOfItems ;                                                         // build the parameter set - set number of queue items
        paramset = paramset + "&Qname='" + Qname + "'";                                                        // set print queue name
        
        RequestBody = HttpRequest.newBuilder()                                                                 // build the http request
        .uri(URI.create(System_Details.SystemUrl + "/sap/opu/odata/SAP/API_CLOUD_PRINT_PULL_SRV/Get_Next_QItemsExt" + paramset)) // add path for function
        .version(HttpClient.Version.HTTP_2)                                                                    // set call context
        .header("x-csrf-token", "fetch")
        .header("Content-Type", "application/json")
        .header("accept", "application/json")
        .header("user-agent", "printqueuequery")
        .header("Authorization", Credentials)
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        RequestHeaders  = RequestBody.headers();                                                               // read the request header for error analysis
        ResponseReply   = HttpClient.newHttpClient().send(RequestBody, HttpResponse.BodyHandlers.ofString());  // initiate http call
        ResponseHeaders = ResponseReply.headers();                                                             // read the reply header for error analysis  
        ResponseCodeL   = ResponseReply.toString();                                                
        ResponseCodeL   = ResponseCodeL.substring(ResponseCodeL.length()-3,ResponseCodeL.length());            // extract http status code
    
        //System.out.println("");
        //System.out.println("Get_Next_QItemsExt");
        //System.out.println("");
        //System.out.println("HttpRequest");    
        //System.out.println(RequestBody);        
        //System.out.println("");
        //System.out.println("RequestHeaders");
        //System.out.println(RequestHeaders);
        //System.out.println("");
        //System.out.println("ResponseReply");    
        //System.out.println(ResponseReply);    
        //System.out.println("");
        //System.out.println("ResponseBody");    
        //System.out.println(ResponseReply.body());
        //System.out.println("");
        //System.out.println("ResponseHeaders");
        //System.out.println(ResponseHeaders);
        //System.out.println("");

        if ( ResponseReply.body().toString().length() > 99 ) {                                                 // check if there is payload
            if ( !ResponseReply.body().toString().contains("{\"d\":{") ) {                                     // check if proper structure is received
				System.out.println("error occured");                                                           // exit in case of elemd "d" does not exist
				System.out.println(ResponseReply.body());    
				System.exit(0);
			}
            JSONObject jObject    = new JSONObject(ResponseReply.body().toString());                           // read the json structure
            JSONArray  jQItemList = jObject.getJSONObject("d").getJSONArray("results");                        // read from json the results structure
            //System.out.println("jQItemList.length: " + jQItemList.length());
            
			for (int i = 0; i < jQItemList.length(); i++) {                                                    // loop on the print queue items

                JSONObject jQItem    = jQItemList.getJSONObject(i);                                            // read from json the print queue item                                                                
                QItemDoc             = jQItem.getString("Documents");                                          // read from json the Documents structure
                JSONArray jDocuments = new JSONArray(QItemDoc);                                                // read the nested json structure
                //System.out.println(jDocuments);
                
				for (int j = 0; j < jDocuments.length(); j++) {
					
					JSONObject jDocumentItem = jDocuments.getJSONObject(j);                                    // read from json the documents for the print queue item
					
					QItemId   = jQItem.getString("QItemId");                                                   // read from json the print queue item ID
					QItemFile = jDocumentItem.getString("document_name");                                      // read from json the print queue item file name
					QItemBlob = jDocumentItem.getString("blob");                                               // read from json extract from the blob
					QItemBlob = QItemBlob.substring(0,3) + "..." + QItemBlob.substring(QItemBlob.length()-3,QItemBlob.length());
					QItemSize = jDocumentItem.getLong("filesize");                                             // read from json the print queue item file size

					//System.out.println("-Documents: " + QItemDoc);
					//System.out.println("-    QItemId:   " + QItemId);         
					//System.out.println("-    QItemFile: " + QItemFile);       
					//System.out.println("-    QItemSize: " + QItemBlob);       
					//System.out.println("-    QItemBlob: " + QItemSize);       
					//System.out.println("");       
						 
					QItemList[i] = new QItem_Str(QItemId, QItemFile, QItemBlob, QItemSize, "", 0);             // return print queue meta data 
					if (j > 0) break;                                                                          // ignore attachemens for now ####
				}
            }
        }
        
        return (Integer.valueOf(ResponseCodeL));                                                               // return the http status code
    }


    public static int PrintItemStatusSet( QItem_Str[] QItemList, int NoOfQItems_Queues, String Qname, String Credentials, System_Str System_Details ) throws Exception {
    // see SAP doumenation for the API
    
        HttpClient   client;                                                                                   // http handle 
        HttpRequest  RequestBody;                                                                              // http request body
        HttpHeaders  RequestHeaders;                                                                           // http request header
        HttpResponse ResponseReply;                                                                            // http reply body
        HttpHeaders  ResponseHeaders;                                                                          // http reply header
        String       ResponseCodeL;                                                                            // http response code
        String       jsonInputString;                                                                          // JSON structure for PrintItemStatusSet
        String       XCsrfToken;                                                                               // XCsrfToken for api call
        String       Getcookie;                                                                                // session cookie for api call     
        
        // Part 0: Create client for common part
        client = HttpClient.newBuilder()                                                                       // Preprare to set cookie context
        .cookieHandler(new CookieManager())
        .version(HttpClient.Version.HTTP_2)
        .build();
        
        // Part 1: query for x-csrf-token
        RequestBody = HttpRequest.newBuilder()                                                                 // build the http request
        .uri(URI.create(System_Details.SystemUrl + "/sap/opu/odata/SAP/API_CLOUD_PRINT_PULL_SRV/PrintItemStatusSet"))  // add path for function
        .version(HttpClient.Version.HTTP_2)                                                                    // set call context
        .header("x-csrf-token", "fetch")
        .header("Content-Type", "application/json")
        .header("accept", "application/json")
        .header("user-agent", "printqueuequery")
        .header("Authorization", Credentials)
        .method("HEAD", HttpRequest.BodyPublishers.noBody())
        .build();

        RequestHeaders  = RequestBody.headers();                                                               // read the request header for error analysis 
        ResponseReply   = client.send(RequestBody, HttpResponse.BodyHandlers.ofString());                      // initiate http call
        ResponseHeaders = ResponseReply.headers();                                                             // read the reply header for error analysis   
        ResponseCodeL   = ResponseReply.toString();                                                
        ResponseCodeL   = ResponseCodeL.substring(ResponseCodeL.length()-3,ResponseCodeL.length());            // extract http status code
    
        //System.out.println("");
        //System.out.println("PrintItemStatusSet");
        //System.out.println("");
        //System.out.println("HttpRequest");    
        //System.out.println(RequestBody);        
        //System.out.println("");
        //System.out.println("RequestHeaders");
        //System.out.println(RequestHeaders);
        //System.out.println("");
        //System.out.println("ResponseReply");    
        //System.out.println(ResponseReply);    
        //System.out.println("");
        //System.out.println("ResponseBody");    
        //System.out.println(ResponseReply.body());
        //System.out.println("");
        //System.out.println("ResponseHeaders");
        //System.out.println(ResponseHeaders);
        //System.out.println("");

        XCsrfToken      = ResponseHeaders.firstValue("X-CSRF-Token").toString();                               // extract required X-CSRF-Token
        XCsrfToken      = XCsrfToken.replace("Optional[", "").replace("]", "");
        //System.out.println("X-CSRF-Token: \"" + XCsrfToken + "\"");
        
        Getcookie       = ResponseHeaders.toString();                                                          // get session cookie
        Getcookie       = Getcookie.substring(Getcookie.indexOf("set-cookie=[")+12);
        Getcookie       = Getcookie.substring(0,Getcookie.indexOf("]"));
        //System.out.println("Cookie:       \"" + Getcookie  + "\"");

        jsonInputString = "{\"Qname\":\"" + Qname + "\",\"ItemsStatus\":\"[";                                  // build the json string for api call
        for (int i = 0; i < NoOfQItems_Queues; i++) {                                                         
            jsonInputString = jsonInputString + "{\\\"ITEM_ID\\\":\\\"";                                      
            jsonInputString = jsonInputString + QItemList[i].QItemId;                                          // add the Print Queue Item ID
            jsonInputString = jsonInputString + "\\\",\\\"STATUS\\\":\\\"S\\\",";                              // set success code
            jsonInputString = jsonInputString + "\\\"STATUS_DESC\\\":\\\"Printed by NN\\\"}";                  // set success message
            if ( i < ( NoOfQItems_Queues - 1 )) {
                jsonInputString = jsonInputString + ",";
            }
        }
        jsonInputString = jsonInputString + "]\"}";                                                            // close the json structure 

        // Part 2: post print item status
        RequestBody = HttpRequest.newBuilder()                                                                 // build the http request
        .uri(URI.create(System_Details.SystemUrl + "/sap/opu/odata/SAP/API_CLOUD_PRINT_PULL_SRV/PrintItemStatusSet")) // add path for function
        .version(HttpClient.Version.HTTP_2)                                                                    // set call context
        .header("X-CSRF-Token", XCsrfToken)                                                                    // add XCsrfToken
        .header("cookie", Getcookie)                                                                           // add session cookie
        .header("Content-Type", "application/json")
        .header("accept", "application/json")
        .header("user-agent", "printqueuequery")
        .header("Authorization", Credentials)
        .method("POST", HttpRequest.BodyPublishers.ofString(jsonInputString))
        .build();

        RequestHeaders  = RequestBody.headers();                                                               // read the request header for error analysis 
        ResponseReply   = client.send(RequestBody, HttpResponse.BodyHandlers.ofString());                      // initiate http call
        ResponseHeaders = ResponseReply.headers();                                                             // read the reply header for error analysis   
        ResponseCodeL   = ResponseReply.toString();                                                
        ResponseCodeL   = ResponseCodeL.substring(ResponseCodeL.length()-3,ResponseCodeL.length());            // extract http status code
        
        //System.out.println("");
        //System.out.println("PrintItemStatusSet POST");
        //System.out.println("");        
        //System.out.println("HttpRequest");    
        //System.out.println(RequestBody);        
        //System.out.println("");
        //System.out.println("RequestHeaders");
        //System.out.println(RequestHeaders);
        //System.out.println("");
        //System.out.println("ResponseReply");    
        //System.out.println(ResponseReply);    
        //System.out.println("");
        //System.out.println("HttpResponseReply");    
        //System.out.println(ResponseReply.body());
        //System.out.println("");
        //System.out.println("ResponseHeaders");
        //System.out.println(ResponseHeaders);
        //System.out.println("");

        XCsrfToken      = ResponseHeaders.firstValue("X-CSRF-Token").toString();                               // extract required X-CSRF-Token
        XCsrfToken      = XCsrfToken.replace("Optional[", "").replace("]", "");
        //System.out.println("X-CSRF-Token: \"" + XCsrfToken + "\"");
        
        return (Integer.valueOf(ResponseCodeL));                                                               // return the http status code
    }
    

    public static int Set_QItems_LastErrorExt( int NumberOfItems, String Qname, String Credentials, System_Str System_Details ) throws Exception {
    // see SAP doumenation for the API
     
        HttpRequest  RequestBody;                                                                              // http request body 
        HttpResponse ResponseReply;                                                                            // http request header
        HttpHeaders  RequestHeaders;                                                                           // http reply body
        HttpHeaders  ResponseHeaders;                                                                          // http reply header
        String       ResponseCodeL;                                                                            // http response code
        String       paramset;                                                                                 // parameter set for api call
        
        paramset = "?Qname='" + Qname + "'";                                                                   // build the parameter set - set the print queue name
        paramset = paramset + "&NumberOfItems=" + NumberOfItems;                                               // set the number of print queue items
        paramset = paramset + "&ResetToStatus='F'";                                                            // set the status failure
        paramset = paramset + "&ResetToStatusDesc='test_for_Set_QItems_LastErrorExt'";                         // set the status message
        RequestBody = HttpRequest.newBuilder()                                                                 // build the http request
        .uri(URI.create(System_Details.SystemUrl + "/sap/opu/odata/SAP/API_CLOUD_PRINT_PULL_SRV/Set_QItems_LastErrorExt" + paramset )) // add path for function
        .version(HttpClient.Version.HTTP_2)                                                                    // set call context             
        .header("x-csrf-token", "fetch")                                                                                   
        .header("Content-Type", "application/json")
        .header("accept", "application/json")
        .header("user-agent", "printqueuequery")
        .header("Authorization", Credentials)
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        RequestHeaders  = RequestBody.headers();                                                               // read the request header for error analysis
        ResponseReply   = HttpClient.newHttpClient().send(RequestBody, HttpResponse.BodyHandlers.ofString());  // initiate http call
        ResponseHeaders = ResponseReply.headers();                                                             // read the reply header for error analysis  
        ResponseCodeL   = ResponseReply.toString();                                                            
        ResponseCodeL   = ResponseCodeL.substring(ResponseCodeL.length()-3,ResponseCodeL.length());            // extract http status code
    
        //System.out.println("");
        //System.out.println("Get_Next_QItemsExt");
        //System.out.println("");
        //System.out.println("HttpRequest");    
        //System.out.println(RequestBody);        
        //System.out.println("");
        //System.out.println("RequestHeaders");
        //System.out.println(RequestHeaders);
        //System.out.println("");
        //System.out.println("ResponseReply");    
        //System.out.println(ResponseReply);    
        //System.out.println("");
        //System.out.println("HttpResponseReply");    
        //System.out.println(ResponseReply.body());
        //System.out.println("");
        //System.out.println("ResponseHeaders");
        //System.out.println(ResponseHeaders);
        //System.out.println("");    
    
        return (Integer.valueOf(ResponseCodeL));                                                               // return the http status code
    }
    
    public record QItem_Att (                                                                                  // structure for single queue item
        String       QItemFile,                                                                                // queue item file name
        String       QItemBlob,                                                                                // queue item blob
        long         QItemSize                                                                                // queue item file size
    ) {}

    public record QItem_Str (                                                                                  // structure for single queue item
        String       QItemId,                                                                                  // queue item ID
        String       QItemFile,                                                                                // queue item file name
        String       QItemBlob,                                                                                // queue item blob
        long         QItemSize,                                                                                // queue item file size
		String       QItemUName,                                                                               // user who created the request
		int          NoOfAtt                                                                                   // number of attachements
    ) {}

    public record System_Str (                                                                                 // structure for system detail + access data
        String      SystemUrl,                                                                                 // URL to SAP system
        String      SystemUser,                                                                                // SAP User Name / Client ID on BTP
        String      SystemPassword,                                                                            // SAP User Password / Client Secret on BTP
        String      SystemType,                                                                                // SAP System Type
        String      SubDomain,                                                                                 // Sub-Account name, only required for BTP
        int         Interval                                                                                    // intervall for ICM cache polling
    ) {}


    // main program
    public static void main(String[] args) throws Exception {
        
        String        Get_IcmCache_old;                                                                        // ICM cache status of prior call
        String        Get_IcmCache_new;                                                                        // ICM cache status of current call
        StringBuilder ICMCache            = new StringBuilder("");                                             // ICM cache status
        String        ApiCall;                                                                                 // Buffer for text for current API call
        int           ResponseCode;                                                                            // Result code of API call
        String        Credentials;                                                                             // Buffer for logon credential
        StringBuilder BearerToken         = new StringBuilder("");                                             // Buffer for bearer token
        System_Str    System_Details[]    = new System_Str[1];                                                 // System details strcture
        String        ListOfQueues[]      = new String[100];                                                   // List of print queues
        int           NumberOfQueues[]    = new int[1];                                                        // Number of print queues
        int           NoOfQItems_Queue[]  = new int[1];                                                        // Number of print queue items in print queue
        int           NoOfQItems_Queues[] = new int[100];                                                      // Number of print queue items per print queue
        QItem_Str     QItemList[]         = new QItem_Str[100];                                                // List of print queue item IDs per queue
        long          timer_cur;                                                                               // Current time stamp
        long          timer_new;                                                                               // time stamp for next planned ICM cache polling
        int           lauf1;                                                                                   // loop variable 1
        int           lauf2;                                                                                   // loop variable 2
        boolean       new_request;                                                                             // flag for new print queue items
        
        
        ResponseCode = Get_System_Details( System_Details );                                                   // get system details
        System.out.println(ResponseCode + " Get_System_Details");        
        
        Credentials = "Basic " + new String(Base64.getEncoder().encode((System_Details[0].SystemUser + ":" + System_Details[0].SystemPassword).getBytes())); // build credentials
        
        ApiCall      = "Get_PrintQueuesOfUserExt";                                                             // prepare api call
        ResponseCode = Get_PrintQueuesOfUserExt(NumberOfQueues, ListOfQueues, Credentials, System_Details[0]); // call api Get_PrintQueuesOfUserExt
        if ( (ResponseCode == 401) & ( System_Details[0].SystemType.equals("SAP BTP")) ) {                     // in case of expired bearer token request new bearer token
            ApiCall      = "Get_Bearer_Token";                                                                 // prepare Get_Bearer_Token
            ResponseCode = Get_Bearer_Token( BearerToken, Credentials, System_Details[0]);                     // call Get_Bearer_Token
            System.out.println(ResponseCode + " " + ApiCall);                                                  // output api call result
            Credentials  = "Bearer " + BearerToken.toString();                                                 // copy bearer token to credentials
            //System.out.println("BearerToken: \"" + Credentials + "\"");        
            if (ResponseCode == 200) {                                                                         // retry api call in case of good bearer token
                ApiCall = "Get_PrintQueuesOfUserExt";                                                          // prepare api call
                ResponseCode = Get_PrintQueuesOfUserExt(NumberOfQueues, ListOfQueues, Credentials, System_Details[0]); // call api Get_PrintQueuesOfUserExt
            }
        }        
        System.out.println(ResponseCode + " " + ApiCall);                                                      // output api call result
        if (ResponseCode != 200)                                                                               // in case of invalid bearer token quit program
            System.exit(0);
        

        for (lauf1 = 0; lauf1 < NumberOfQueues[0]; lauf1++) {                                                  // output of all print queues
            System.out.println("    Queue:" + ListOfQueues[lauf1]); 
        }
        
        if ( System_Details[0].SystemType.equals("SAP BTP") ) 
            Get_IcmCache_old = "SCP 1704067200000";                                                            // on BTP: seconds since 1.1.1970 on 1.1.2024
        else 
            Get_IcmCache_old = "19000101000000.0000000";                                                       // on SAP S/4 HANA (any type) initial date+time

        new_request = false;                                                                                   // set flag for new print queue items to false
        Get_IcmCache_new = Get_IcmCache_old;                                                                   // set flag for new print queue items to false
        timer_cur = java.time.Instant.now().getEpochSecond();                                                  // get current time stamp
        timer_new = timer_cur;                                                                                 // make sure that next ICM cache polling take place immediately 
        
        while (true) {                                                                                         // loop forever
        
            timer_cur = java.time.Instant.now().getEpochSecond();                                              // read current time-stamp
            if (timer_cur >= timer_new) {                                                                      // check if next ICM cache polling reached
                timer_new = timer_cur + System_Details[0].Interval;                                            // set time for next ICM cache polling 
                ApiCall      = "ICMCache";                                                                     // prepare api call
                ResponseCode = Get_IcmCache(ICMCache, Credentials, System_Details[0]);                         // call api Get_IcmCache
                Get_IcmCache_new = ICMCache.toString();                                                        // convert ICM cahce status to string
                System.out.println( ResponseCode + " " + ApiCall + ": \"" + Get_IcmCache_new + "\"" );         // output api call result 
                if ( Get_IcmCache_new.equals(Get_IcmCache_old) == false ) {                                    // compare old and new ICM cache status 
                    Get_IcmCache_old = Get_IcmCache_new;                                                       // in case of ICM cache status set flag for new print queue items
                    new_request = true;                                                                         
                }
            }
            else {
                System.out.print("\r" + (timer_new - timer_cur) + "  \r");                                     // output countdown until next ICM cache status polling
                TimeUnit.SECONDS.sleep(1);                                                                     // wait for 1 second 
            } 
                        
            if (new_request == true) {                                                                                                                     // incase of new print queue items
                new_request = false;                                                                                                                       // delete flag for new print queue items
                System.out.println("    ICM cache changed -> new request(s)");                                                                             // output ICM cache change
                for (lauf1 = 0; lauf1 < NumberOfQueues[0]; lauf1++) {                                                                                      // loop on print queues
                    ApiCall      = "NoOfQItems_Queue  ";                                                                                                   // prepare api call
                    ResponseCode = Get_NumberOfQItemsExt(NoOfQItems_Queue, ListOfQueues[lauf1], Credentials, System_Details[0]);                           // call api Get_NumberOfQItemsExt
                    NoOfQItems_Queues[lauf1] = NoOfQItems_Queue[0];                                                                                        // copy number of print queue items in print queue
                    if ( NoOfQItems_Queues[lauf1] > 100 ) {
                        NoOfQItems_Queues[lauf1] = 100;                                                                                                    // if number of print queue items in print queue larger then 100 reduce to 100
                        new_request = true;                                                                                                                // do next queue item query on next loop
                    }
                    System.out.println(ResponseCode + " " + ApiCall + " for Queue: " + String.format("%-20s",ListOfQueues[lauf1]) + " : " + String.format("%3s",NoOfQItems_Queues[lauf1])); // output number of print queue items in print queue
                    if ( NoOfQItems_Queues[lauf1] > 0 ) {                                                                                                  // check of there are queue items in print queue
                        ApiCall      = "Get_Next_QItemsExt";                                                                                               // prepare api call
                        ResponseCode = Get_Next_QItemsExt(QItemList, NoOfQItems_Queues[lauf1], ListOfQueues[lauf1], Credentials, System_Details[0]);  // call api Get_Next_QItemsExt
                        System.out.println(ResponseCode + " " + ApiCall + " for Queue: " + String.format("%-20s",ListOfQueues[lauf1]) + " : " + String.format("%3s",NoOfQItems_Queues[lauf1])); // output api call result 
                        for (lauf2 = 0; lauf2 < NoOfQItems_Queues[lauf1]; lauf2++) {                                                                       // output queue items details
                            System.out.println("    QItemId:   " + QItemList[lauf2].QItemId);                                                              // queue item ID
                            System.out.println("    QItemFile: " + QItemList[lauf2].QItemFile);                                                            // queue item file name
                            System.out.println("    QItemSize: " + QItemList[lauf2].QItemSize );                                                           // queue item blob
                            System.out.println("    QItemBlob: " + QItemList[lauf2].QItemBlob );                                                           // queue item file size
                            System.out.println("");            
                        }
                                                
                        if (true) {                                                                                                                        // Either set transfer success "true" or set transfer failed "false"
                            ApiCall      = "PrintItemStatusSet";                                                                                           // prepare api call
                            ResponseCode = PrintItemStatusSet(QItemList, NoOfQItems_Queues[lauf1], ListOfQueues[lauf1], Credentials, System_Details[0]);   // call api PrintItemStatusSet
                            System.out.println(ResponseCode + " " + ApiCall + " for Queue: " + ListOfQueues[lauf1]);                                       // output result of api call
                        }
                        else {
                            ApiCall      = "Set_QItems_LastErrorExt";                                                                                      // prepare api call
                            ResponseCode = Set_QItems_LastErrorExt(NoOfQItems_Queues[lauf1], ListOfQueues[lauf1], Credentials, System_Details[0]);         // call api Set_QItems_LastErrorExt
                            System.out.println(ResponseCode + " " + ApiCall + ": " + ListOfQueues[lauf1] + "for " + NoOfQItems_Queues[lauf1] + " items");  // output result of api call
                        }
                    } // if ( NoOfQItems_Queues
                } // for (lauf1
            } // if (new_request
        } // while (true
    } // main
} // class SAP_COM_0466_example

Regards
Martin