© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2023
A. S. BluckIBM Software Systems Integration https://doi.org/10.1007/978-1-4842-8861-0_6

6. PDF Document Creation Using Java

Alan S. Bluck1  
(1)
Ashley Heath, Hampshire, UK
 

This chapter covers the step-by-step procedure to develop Java programs to generate PDF documents using the iText library.

This is shown first with simple code steps to
  • Add an Image to a PDF

  • Create a PdfWriter object

  • Create a PdfDocument object

  • Create the Document object

  • Create an Image object

  • Add the image to the Document

  • Close the Document

  • Add a Table to a PDF

Chapter Organization

This chapter contains the following four Parts:

Part 1 – Bill of Materials. This Part describes the iText import packages used in the example pdf generation calls used in this chapter. It also shows some simple example Java code used to demonstrate the calls which can be made.

Part 2 – Example 3 – An Audit Report from the Audit Master. This Part lists the main Audit Report program Java Code.

Part 3 – Supporting Java Classes for the Main Audit Report Program. In this Part, the Java Code is listed for the supporting methods used by the Audit Report program.

Part 4 – Example 4 – Create an Auditor Calendar Table in a PDF Document. This Part demonstrates the calls required to call the iText methods to create a pdf Table structure.

See the following URL link for a tutorial provided by IBM on the use of Java for program development for IBM FileNet systems:

www.ibm.com/docs/en/filenet-p8-platform/5.5.x?topic=transport-adding-connection-code (“Adding Connection Code – IBM Documentation”)

An overview of the API concepts is also covered in this link:

www.ibm.com/docs/en/filenet-p8-platform/5.5.x?topic=guide-getting-started#gs_concepts__gs_requirements

IBM C# .NET Web Service Code Examples can be found at the following URL link:

www.ibm.com/docs/en/filenet-p8-platform/5.5.x?topic=guide-code-examples

The following IBM Redbook link is available:

www.redbooks.ibm.com/abstracts/sg247743.html?Open

Note

This last URL link is Archive documentation, but some sections are (surprisingly) still useful as the IBM FileNet API is relatively stable, and many of the Content Engine Architecture concepts are still relevant.

The following site is run by Ricardo Belfor:

https://ecmdeveloper.com/plugin/intro/

It is described as follows:

ECM Developer is an open-source Eclipse plug-in aimed at supporting the development of applications using the IBM FileNet P8 Content Engine.”

Note

“The old version also supported CMIS repositories, but due to limited time and resources this is no longer the case. The focus will be on IBM FileNet P8 Content Engine repositories.”

This can be downloaded from the URL page: https://ecmdeveloper.com/plugin/getting-started/

“This first step is to download the software for the plug-in. The plug-in can be downloaded at https://ecmdeveloper.com/eclipse-plugin/com.ecmdeveloper.plugin.repository-2.3.0.zip or by using the Eclipse update site https://ecmdeveloper.com/eclipse-plugin.”

I have a ResearchGate publication as follows for IBM FileNet Java Code development:

“IBM FileNet P8 Java Development on ECM Cloud Private Container P8 Examples”

https://doi.org/10.13140/RG.2.2.20160.69129

Part 1 – Bill of Materials

The Java build used in this chapter is largely based on the Eclipse IDE and support jar files we used in Chapter 2.

Imports Used with the Test Audit Report Stub from the iText jar Library

The following code package import statements are used for the iText pdf generation examples:
import com.lowagie.text.Document;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Image;
import com.lowagie.text.PageSize;
import com.lowagie.text.Rectangle;
import com.lowagie.text.Anchor;
import com.lowagie.text.BadElementException;
import com.lowagie.text.Chapter;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.List;
import com.lowagie.text.ListItem;
import com.lowagie.text.Phrase;
import com.lowagie.text.Section;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.pdf.RandomAccessFileOrArray;
import com.lowagie.text.pdf.codec.TiffImage;

Example 1 – A Simple Audit PDF Document

The following example code uses the iText paragraph object and sets the pdf Header metadata settings.
package com.ibm.filenet.ps.ciops.test;
//Java I/O library imports
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.Date;
//iText jar Library imports
import com.lowagie.text.Document;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.PdfWriter;
import java.time.LocalDateTime; // import the LocalDateTime class
import java.time.LocalDate; // import the LocalDate class
import java.time.LocalTime; // import the LocalTime class
import java.time.format.DateTimeFormatter; // import the Java Date formatter class
/**
* Java Program to create a Simple Audit PDF document using the iText library.
*
* @author Alan S. Bluck
*/
public class AuditTest {
public static void main(String args[]) {
//Set up the Date / Time system variables
LocalDate AuditReportDate = LocalDate.now();
LocalDateTime AuditReportDateTime = LocalDateTime.now();
LocalTime AuditReportTime = LocalTime.now();
DateTimeFormatter AuditDateFormat = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
String sAuditDate = AuditDateFormat.format(AuditReportDateTime);
OutputStream AuditPDFfile = null;
try {
     AuditPDFfile = new FileOutputStream(new File("/opt/AuditReport/AuditTest.pdf"));
     //Create a new Audit Document object
     Document audit_document = new Document();
     //You need the iText PdfWriter class for a PDF document
     PdfWriter.getInstance(audit_document, AuditPDFfile);
     //Open the Audit document for writing a PDF
     audit_document.open();
     //Write the test content
     audit_document.add(new Paragraph("Audit Report: Document Test PDF Paragraph"));
     audit_document.add(new Paragraph("Auditor Name: R. Jones"));
     audit_document.add(new Paragraph("Audit Date: " + sAuditDate));
     //Add Header meta-data information to the PDF file
     audit_document.addCreationDate();
     audit_document.addAuthor("ASB Software Development Limited");
     audit_document.addTitle("Audit Report Test PDF Generation");
     audit_document.addCreator("Program AudiTest");
     //close the document
     audit_document.close();
     System.out.println("Audit Report Created:" + sAuditDate);
     } catch (Exception e) {
          e.printStackTrace();
     } finally {
//close the FileOutputStream
          try {
               if (AuditPDFfile != null) {
                    AuditPDFfile.close();
               }
          } catch (IOException io) {
               //Failed to close
               System.out.println("The Audit PDF file failed to close!");
               }
          }
     }
}
Listing 6-1

The AuditTest.java test pdf Text creation code

Expected Output from the AuditTest.java Code

As shown in the preceding code, the generated PDF can be set with a metadata Header. This is a standard PDF format which can be used to set attributes such as the pdf author name, a title, a file description, etc. This is a useful additional feature for use with an Auditing system, where traceability is important (especially since the Audit system itself is audited by the Auditing standards body!).

Adding an Image to a PDF Document

An empty PDF Document is created using the Document class. A PdfDocument object is then passed as an argument to the Document class constructor. To add an image to the PDF, we can create an object of the image that is required to be added and add this image using the add() method of the Document class.

The following are the steps to add a TIFF image to the PDF document.

Note

I am using iText version 2.1.7 for this chapter; there is a new iText version 7 available which has more features (support for jpeg images, for example), but the 2.1.7 version is supported by an Apache 2.0 license (embedded in the com.lowagie.text-2.1.7.jar file) and is fine for the application we are using, and the whole system is held in this one .jar file.

Later versions are split into multiple .jar files, and some of the Java classes are coded differently.

Creating a PdfWriter Object

The PdfWriter class is a Java DocWriter class for a PDF. This class is found in the Java package com.lowagie.text.pdf. The constructor of this class accepts a string containing the path of the file where the PDF is to be created.

The PdfWriter class is created by first passing a string value file path (defining the folder path of the PDF file) as shown in Listing 6-2.
// Create a PdfWriter
     String sTargetPDFFile = "/opt/AuditReport/AuditTest/AuditImage.pdf";
     File pdfFile = new File(sTargetPDFFile);
     com.lowagie.text.Document document = new com.lowagie.text.Document(PageSize.A3.rotate(), 50, 50, 100, 100);
Listing 6-2

Code snippet for creating the iText Document Class

When an object of this type is passed to an iText Document Java class, every element added to the document object will be written to the file we defined earlier.

Creating an iText Document Object Class

The Document Java class object holds an image of the PDF Document used in iText. This Java class is defined in the package com.lowagie.text.Document. To instantiate this class, a PdfWriter class object is passed to the Document Java class. This relationship can be demonstrated using the Java code in Listing 6-3.
     PdfWriter AuditPDFwriter = PdfWriter.getInstance(document, new FileOutputStream(pdfFile));
// Create a PdfDocument
      document.open();
Listing 6-3

Code to create an iText PdfWriter class

After a PDF Document class object is created, you can add the elements like page, font, file attachment, and event handler using iText methods provided by the class as shown in Listing 6-4 for adding sections of Auditor questions.
//AUDITOR START OUTPUT TO PDF
for(int iSection=0 ; iSection < noAUDITORSections ; iSection++) {
// We add one empty line
addEmptyLine(sections, 1);
// Lets write a big header
//TODO Pick all text up from the config.xml file
Paragraph paragraph = new Paragraph(TabSECTIONS_AUDITOR[iSection], catFont);
    paragraph.setAlignment(Element.ALIGN_CENTER);
    sections.add(paragraph);
    addEmptyLine(sections, 1);
for(int iProp=0 ; iProp < noAUDITORprops[iSection] ; iProp++) {
    paragraph = new Paragraph(AUDITOR_propNames[iSection][iProp] + " : " + AUDITOR_propValues[iSection][iProp], catFont);
paragraph.setAlignment(Element.ALIGN_CENTER);
    sections.add(paragraph);
    addEmptyLine(sections, 1);
    }
    }
    document.add(sections);
    // Start a new page
    document.newPage();
Listing 6-4

Code to add paragraphs, sections, and New pages to a pdf document

Creating the Document Object

The Document class defined in the Java package com.lowagie.text.Document is the root element of the PDF image object.

You can instantiate the Document class by passing an object of the class PdfWriter (in package com.lowagie.text.pdf) created in the previous steps, as shown in Listing 6-5, which can be set to a specific PDF version (we selected version 1.3).
// Creating an Audit Report Document as class Document
     AuditPDFwriter.setPdfVersion(PdfWriter.VERSION_1_3);
Listing 6-5

Code to set the version of PDF generated

Creating an Image Object

To create the TiffImage class object, we can use the package com.lowagie.text.pdf.codec and then use the iText RandomAccessFileOrArray class object class in the constructor method. As an argument of the RandomAccessFileOrArray constructor method, we pass a string argument defining the path of the image, as shown in Listing 6-6.
// Creating an Image object
     String imageLogoFile = "/opt/AuditReport/images/AuditMasterLogo.tif";
     RandomAccessFileOrArray ra = new RandomAccessFileOrArray(imageLogoFile);
     int pages = TiffImage.getNumberOfPages(ra);
Listing 6-6

Code to create an iText Image object RandomAccessFileOrArray

Now we can instantiate the Image class of the iText library package, com.lowagie.text.Image. In the constructor, we can pass the preceding RandomAccessFileOrArray class object as an argument, as shown in Listing 6-7.
// Creating an Image object
Image image;
image = TiffImage.getTiffImage(ra, 1);
Listing 6-7

Code to create the iText Image class object

Adding Images to the Document

We can now add the image object created in the previous step using the add() method of the Document class, as shown in Listing 6-8.
// Adding the Audit Master Logo image to the Audit report document
    Rectangle pageSize = new Rectangle(image.getWidth(),
    image.getHeight());
    document.setPageSize(pageSize);
    document.add(image);
Listing 6-8

Code to set the page size and add the iText Image object to a document object

Closing the Document

Finally, we must close the document using the close() method of the Document class, as shown in Listing 6-9.
// Save to the Linux folder path /opt/AuditMaster/AuditTest
// Closing the Audit report document
document.close();
      System.out.println("Audit Master Logo Image added");
   }
}
Listing 6-9

Code to close the pdf Document object

Example 2 – Test Code to Add the Audit Master Logo

The following Test Java code was used to add the Audit Master Logo TIFF image to a PDF document using the iText library. It creates a PDF Audit Report document with the name AuditImage.pdf, adds the Audit Master TIFF image to it, and saves it in the Linux folder path /opt/AuditReport/AuditTest/.

We have saved this code in a file with name AuditMasterLogoTest.java.
package com.ibm.filenet.ps.ciops.test;
import java.io.File;
import java.io.FileOutputStream;
// iText jar Library imports
import com.lowagie.text.Document;
import com.lowagie.text.Image;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfDocument;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.pdf.RandomAccessFileOrArray;
import com.lowagie.text.pdf.codec.TiffImage;
public class AuditMasterLogoTest {
   public static void main(String args[]) throws Exception {
// Create a PdfWriter
     String sTargetPDFFile = "/opt/AuditReport/AuditTest/AuditImage.pdf";
     File pdfFile = new File(sTargetPDFFile);
     com.lowagie.text.Document document = new com.lowagie.text.Document(PageSize.A3.rotate(), 50, 50, 100, 100);
     PdfWriter AuditPDFwriter = PdfWriter.getInstance(document, new FileOutputStream(pdfFile));
// Create a PdfDocument
      document.open();
// Creating an Audit Report Document as class Document
     AuditPDFwriter.setPdfVersion(PdfWriter.VERSION_1_3);
// Creating an ImageData object
     String imageLogoFile = "/opt/AuditReport/images/AuditMasterLogo.tif";
     RandomAccessFileOrArray ra = new RandomAccessFileOrArray(imageLogoFile);
     int pages = TiffImage.getNumberOfPages(ra);
// Creating an Image object
     Image image;
    image = TiffImage.getTiffImage(ra, 1);
// Adding the Audit Master Logo image to the Audit report document
    Rectangle pageSize = new Rectangle(image.getWidth(),
    image.getHeight());
    document.setPageSize(pageSize);
    document.add(image);
// Save to the Linux folder path /opt/AuditMaster/AuditTest
// Closing the Audit report document
document.close();
      System.out.println("Audit Master Logo Image added");
   }
}
Listing 6-10

Complete example Java code to add a TIFF image to a pdf Document

Part 2 – Example 3 – An Audit Report from the Audit Master

A complete program to provide a pdf Audit report from the Audit questions from the Audit Master using Cases from the solution created in Chapter 1 is as follows.

We added Audit questions as follows:

Check management responsibility
  1. 1.

    Check quality system.

     
  2. 2.

    Latest/relevant issue of documentation available.

     
  3. 3.

    Authorization of documentation.

     
  4. 4.

    No unauthorized additions/alterations to documentation.

     
  5. 5.

    Documentation marked with issue status.

     
  6. 6.

    Procedures manual exists and is followed.

     
  7. 7.

    No uncontrolled documents present.

     
  8. 8.

    Transaction records are used for identification.

     
  9. 9.

    Inventory records are kept.

     
  10. 10.

    System for determining the current status of parts in production.

     
  11. 11.

    Correct paperwork accompanying parts during the production process.

     
  12. 12.

    Evidence of the use of procedures and work instructions.

     
  13. 13.

    Measuring equipment is calibrated.

     
  14. 14.

    Procedures for in-process inspection.

     
  15. 15.

    Identification of jigs and tools.

     

Creating the Audit_Report Java Project

A cropped screenshot of an activities window. It has a toolbar, in which the new option is selected under the file, and the project is selected under the run.

Figure 6-1

A new Java Project is selected in the Eclipse IDE

A screenshot of a new project window. It displays the options under the wizards, where the java project is selected, and the next button is highlighted.

Figure 6-2

The Java Project type is selected

For compatibility with the FileNet jar files, we select the same JRE Library we used for the Java in Chapter 2.

A new java project window displays the options to create a java project, where use a project-specific J R E, java dash x 86 underscore 64 - 80, and the next button is highlighted.

Figure 6-3

The JRE is selected for compatibility with the FileNet API jar files

A screenshot of a new java project window. It displays the java settings options, where the audio report is selected under the source, and the finish button is highlighted.

Figure 6-4

The name is set as Audit_Report

A pop-up window asks if we want to use the Eclipse IDE Java perspective, which automatically sets a number of window pane views and a tree browser pane on the left of the window to allow the program classes to be split into a logical package structure for the Java project.

A screenshot of an open associated perspective popup box. It displays a question to open the java perspective and the open perspective button is highlighted.

Figure 6-5

The Java perspective is selected

Creating the com.asb.ce.utils Java Package

A cropped screenshot of a page displays a list under the audit report, where s r c is selected and it lists a menu, where new is selected, and further package is selected.

Figure 6-6

The New Package option is selected for the Audit Report program

In Figure 6-6, we select a new class Package to add supporting Java code for the main Audit Report program.

A screenshot of a new java package window. It displays the options to create a new java package, where the name is given as, c o m dot a s b dot c e dot utils, and the finish button is highlighted.

Figure 6-7

The Package name is entered as com.asb.ce.utils

Underneath the com.asb.ce.utils package (which is actually an Eclipse nested folder structure), we now add a Java class, which will hold the Java code we need for some utility class methods.

Creating the AuditReportConfig Java Class

A screenshot of a page displays a list under the audit report, where the file c o m dot a s b dot c e dot utils is selected, lists a menu, where new is selected, and further class is selected.

Figure 6-8

The right-clicked package gives the New ➤ Class menu

We create an AUDITReportConfig class for Java code which will read in the parameters we require to drive the Audit Report program. These are held in a config.xml file which we structure to make entry of the variables easier and to allow the right values to be read for each of the program variables (and to avoid hard-coding for maximum flexibility).

The config.xml file path is defined for access using a platform-independent section of Java code which detects if this is a Windows-based system or a Linux/Unix-based system directory path structure, as shown in the following Java code snippet.
//ASB ... 11-08-2022 - Make platform independent
String OS = System.getProperty("os.name").toLowerCase();
String file;
if (OS.indexOf("win") >= 0)  {
     file = System.getProperty("user.dir") + "\config\config.xml";
} else { //Set to forward slash
     file = System.getProperty("user.dir") + "/config/config.xml";
}
Listing 6-11

The section of code to read the config.xml with platform independence

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, audit report config, and the finish button is highlighted.

Figure 6-9

The AUDITReportConfig class is defined for entry of the code

The code in Listing 6-12 is entered next for the AuditReportConfig.java.
package com.asb.ce.utils;
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions. IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you "AS IS"
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
     /**
      *
      */
     /**
      * @author Alan S. Bluck, ASB Software Development Limited
      *
      */
     import java.io.BufferedWriter;
     import java.io.File;
     import java.io.FileInputStream;
     import java.io.FileWriter;
     import java.io.IOException;
     import java.io.InputStream;
     import java.io.StringWriter;
     import java.text.DateFormat;
     import java.text.ParseException;
     import java.text.SimpleDateFormat;
     import java.util.Calendar;
     import java.util.Date;
     import java.util.Locale;
     import java.util.Vector;
     import javax.xml.parsers.DocumentBuilder;
     import javax.xml.parsers.DocumentBuilderFactory;
     import javax.xml.parsers.ParserConfigurationException;
     import javax.xml.transform.OutputKeys;
     import javax.xml.transform.Transformer;
     import javax.xml.transform.TransformerFactory;
     import javax.xml.transform.dom.DOMSource;
     import javax.xml.transform.stream.StreamResult;
     import javax.xml.xpath.XPath;
     import javax.xml.xpath.XPathConstants;
     import javax.xml.xpath.XPathFactory;
     import org.apache.commons.codec.binary.Base64;
     import org.apache.log4j.Logger;
     import org.w3c.dom.Document;
     import org.w3c.dom.Node;
     import org.w3c.dom.NodeList;
     import org.xml.sax.SAXException;
import com.asb.config.AUDITConfig;
     public class AUDITReportConfig {
          Logger logger = Logger.getLogger(AUDITReportConfig.class.getName());
          private Document dom;
          // Export Parameters
          private String SolutionPrefix;
          private String AUDITORMainTask;
          private String exportOSName;
          private String exportCEUser;
          private String exportCEPassword;
          private String exportCEUrl;
          //ASB Added for Workflow System calls 10-08-2022
        private String exportConnectionPoint;
          private String exportCEStanza;
          private String exportWaspLocation;
          private String MimeType;
         private String QSelectList;
          private String QWhereClause;
         private String QOrderByClause;
         //ASB 17-05-2022 add Folder Clause strings for Case Folder queries
          private String QCaseFolderOrderByClause;
         private String QCaseFolderWhereClause;
         private String ReportDirectoryName;
         private String TestFolderName;
     //ASB ...  add Document propertyList values
          private String exportAuditFile;
          private Integer maximumDocs;
          private Integer noQQuestions;
          private Integer noQColumns;
          //Loader
          private String[] propNames;
          private String[] propFlags;
         private String[] propTypes;
         private String[] propCardinality;
          // Import parameters
          private String importOSName;
          private String importOSRootFolder;
          private String docPathName;
          private String CEFolderPath;
          private String DocClassName;
          private String ReportPath;
          private String ReportExtension; //ASB 11-08-2022 Added for report files
          private String importCEUrl;
          private String importCELoginConfig; //ASB
          private String importCEStanza;
          private String importCEUser;
          private String importCEPassword;
          private String importAuditFile;
          //ASB Define the new Email getter/setter values 11-08-2022
          private String sourceEmailAddress;
          private String sourceEmailPassword;
          private String SMTPTLSRequired;
          private String SMTPSSLRequired;
          private String SMTPUser;
          private String TLSPort;
          private Integer emailTLSPort;
          private String SSLPort;
          private Integer emailSSLPort;
          private String SMTPType;
          private String SMTPHost;
          private String SMTPEmailTemplates;
          private Integer importMaxErrorCount;
          private Integer importMaxRunTimeMinutes;
          private String exportCELoginConfig;
          private Integer importMaxDocCount;
          private String importAuditFileFolder;
          private Integer updatingBatchSize;
          private String caseStatus; //ASB  - Search status for GUID deletion
          private String caseStatusField; //ASB  - Search status Field Name for GUID deletion
          private String[] PropCardinality;
          private String EmailListName;
          private String EmailFlagListName;
          private String  STOPState;
          private String  ChoiceListFlag;
          private String  startSearchDate;
          private String  QstartSearchDate;  //ASB 11-08-2022
          private String  QSearchDays;        //ASB 11-08-2022
         private String  SearchSubClasses;  //ASB Flag set to YES will search the subclasses of the Document class used as the root class
          private String  deltaSearchDate;   //ASB           Calculated End Date/Time
          private String  deltaHours = "01"; //ASB Delta for "  "  "
          private Integer  SleepTime = 2;    //ASB Delta for "  "  "
          private String  deltaMinutes = "01"; //ASB Delta for "  "  "
          private String  searchProperty = "Id"; //ASB Symbolic property name for Delete search
          private String  searchPropertyDocs = "Id"; //ASB Symbolic property name for Delete search
          private String  searchPropertyFolders = "CmAcmCaseFolder"; //ASB Symbolic property name for Delete search
          private Integer maxSearchSize;
          //ASB ...   Debug Flag for performance improvement
          private String debugFlag;
         //ASB ...   Get LDAP Search Flag for performance improvement
          private String LDAPSearchFlag;
         //ASB ...   Get Case Folder Class list
          private String folderCaseClasses;
          //ASB ...   Get JDBC Database info
          private String JDBC;
          private String JDBCDriverClass;
          private String JDBCUser;
          private String JDBCPassword;
          private XPath xPath;
          public static void main(String args[]) throws Exception {
               AUDITReportConfig cemc = new AUDITReportConfig();
          }
          public  AUDITReportConfig() throws Exception {
               XPathFactory  factory= XPathFactory.newInstance();
               xPath=factory.newXPath();
               xPath.reset();
               getDocument();
               readConfig();
          }
          private void readConfig() throws Exception {
     //ASB ...  Deletion Configuration Parameters
     //readOnlyGroup = getXMLVal("/config/AUDITProcessor/readonlygroup/text()");
     SolutionPrefix = getXMLVal("/config/AUDITProcessor/SolutionPrefix/text()"); //CHECK
     //excludedGroup = getXMLVal("/config/AUDITProcessor/excludedgroup/text()");
     //excludedUser = getXMLVal("/config/AUDITProcessor/excludeduser/text()");
     startSearchDate = getXMLVal("/config/AUDITProcessor/startsearchdate/text()");
     //ASB 18-05-2022 For Calculation of QstartSearchDate
     QSearchDays = getXMLVal("/config/AUDITProcessor/searchdays/text()");
     maxSearchSize = Integer.parseInt(getXMLVal("/config/AUDITProcessor/MaxSearchSize/text()"));
     noQQuestions = Integer.parseInt(getXMLVal("/config/NumberOfFullQQuestions/text()"));
     noQColumns = Integer.parseInt(getXMLVal("/config/NumberOfFullQColumns/text()"));
     SleepTime = Integer.parseInt(getXMLVal("/config/SleepTime/text()"));
     //ASB ...  Retrieve Debug Output flag (off/on) value
     debugFlag = getXMLVal("/config/AUDITProcessor/DebugOutputFlag/text()");
     //ASB ...  Retrieve LDAP Search flag (off/on) value
     //LDAPSearchFlag = getXMLVal("/config/AUDITProcessor/LDAPSearchFlag/text()");
     deltaHours = getXMLVal("/config/AUDITProcessor/DeltaHours/text()");
     deltaMinutes = getXMLVal("/config/AUDITProcessor/DeltaMinutes/text()");
     searchProperty = getXMLVal("/config/AUDITProcessor/searchProperty/text()");
     //ASB 18-01-2022 Added for proc processing
     searchPropertyDocs = getXMLVal("/config/AUDITProcessor/searchPropertyDocuments/text()");
     searchPropertyFolders = getXMLVal("/config/AUDITProcessor/searchPropertyFolders/text()");
     //ASB ...  Add FolderSubclasses retrieval
     folderCaseClasses = getXMLVal("/config/AUDITProcessor/folderCaseClasses/text()"); //CHANGE
     //JDBC Parameters for the Database
     JDBC = getXMLVal("/config/AUDITProcessor/JDBC/text()");
     JDBCDriverClass = getXMLVal("/config/AUDITProcessor/JDBCDriverClass/text()");
     JDBCUser = getXMLVal("/config/AUDITProcessor/JDBCUser/text()");
     JDBCPassword = getXMLVal("/config/AUDITProcessor/JDBCPassword/text()");
     //Email
     EmailListName = getXMLVal("/config/AUDITProcessor/EmailListName/text()");
     EmailFlagListName = getXMLVal("/config/AUDITProcessor/EmailFlagListName/text()");
     //ASB 13-08-2022 Added parameters for the pdf file import processing
     importMaxRunTimeMinutes = Integer.parseInt(getXMLVal("/config/ceimport/MaxRunTimeMinutes/text()"));
     importAuditFile = getXMLVal("/config/ceimport/AuditFile/text()");
     importAuditFileFolder= getXMLVal("/config/ceimport/AuditFileFolders/text()");
     //ASB ...  Check if this is still clear ie
     //ASB ...  Not in the form Encrypt3dpasswordvalueP4ssw0rd
     String decryptedJDBCPassword = decrypt(JDBCPassword);
     if (decryptedJDBCPassword.startsWith("Encrypt3d")&& decryptedJDBCPassword.endsWith("P4ssw0rd")  ){
     //ASB ...  Extract the actual password
     //
          JDBCPassword = decryptedJDBCPassword.substring(9, decryptedJDBCPassword.length()-8);
     }else{
     //ASB ...  update the clear password
     String encryptedJDBCPassword = "Encrypt3d" + JDBCPassword + "P4ssw0rd";
     encryptedJDBCPassword = encrypt(encryptedJDBCPassword);
     System.out.print(" ENCRYPTED JDBC CDDR PASSWORD = " + encryptedJDBCPassword);
     System.out.print(" ");
     // ASB ...  Write encrypted password back to the config.xml file
     String dirPath = System.getProperty("user.dir");
     String OS = System.getProperty("os.name").toLowerCase();
     String file = null;
     // ASB
     if (OS.indexOf("win") >= 0)  {
       file = dirPath + "\config\config.xml";
     } else { //Set to forward slash
       file = dirPath + "/config/config.xml";
     }
      // SET    file Name, root element, tag element, old value, new value
      updateXML(file,"AUDITProcessor","JDBCPassword", JDBCPassword, encryptedJDBCPassword);
     }
// Export Parameters
     exportOSName = getXMLVal("/config/ceexport/osname/text()");
     exportConnectionPoint = getXMLVal("/config/ceexport/ConnectionPointName/text()");
     exportCEUser = getXMLVal("/config/ceexport/ceuser/text()");
     exportCEPassword = getXMLVal("/config/ceexport/cepassword/text()");
     importMaxDocCount = Integer.parseInt(getXMLVal("/config/ceimport/MaxDocCount/text()"));
     docPathName = getXMLVal("/config/ceexport/DocPathName/text()");
     DocClassName =  getXMLVal("/config/ceexport/DocClassName/text()");
     caseStatus =  getXMLVal("/config/ceexport/CaseStatus/text()");
     caseStatusField =  getXMLVal("/config/ceexport/CaseStatusField/text()");
     SearchSubClasses =  getXMLVal("/config/database/SearchSubClasses/text()"); //ASB 11-08-2022
     MimeType =  getXMLVal("/config/ceexport/MimeType/text()");
     AUDITORMainTask = getXMLVal("/config/AUDITOR_Task/text()");
     QSelectList = getXMLVal("/config/AUD_Select_List/text()");
     QWhereClause = getXMLVal("/config/AUD_where_clause/text()");
     QOrderByClause = getXMLVal("/config/AUD_orderby_clause/text()");
     //ASB Add code to retrieve new Order and Where query clauses for Case Folder search 11-08-2022
     QCaseFolderWhereClause = getXMLVal("/config/QCase_Folder_where_clause/text()");
     QCaseFolderOrderByClause = getXMLVal("/config/QCase_Folder_orderby_clause/text()");
     ReportDirectoryName = getXMLVal("/config/ReportDirectoryName/text()");
     STOPState = getXMLVal("/config/SET_TO_STOP/text()");
     ChoiceListFlag = getXMLVal("/config/ChoiceListProcessor/SET_TO_GENERATE_CHOICELISTS/text()");//ChoiceListFlag
     TestFolderName = getXMLVal("/config/TestFolderName/text()");
     // ASB New Email Parameters 11-08-2022
     sourceEmailAddress = getXMLVal("/config/SMTPreport/sourceEmailAddress/text()");
     sourceEmailPassword = getXMLVal("/config/SMTPreport/sourceEmailPassword/text()");
     SMTPTLSRequired = getXMLVal("/config/SMTPreport/SMTPTLSRequired/text()");
     SMTPSSLRequired = getXMLVal("/config/SMTPreport/SMTPSSLRequired/text()");
     SMTPHost = getXMLVal("/config/SMTPreport/SMTPHost/text()");
     SMTPUser = getXMLVal("/config/SMTPreport/SMTPUser/text()");
     SMTPHost = getXMLVal("/config/SMTPreport/SMTPHost/text()");
     SMTPEmailTemplates = getXMLVal("/config/SMTPreport/EmailTemplatePath/text()");
     TLSPort = getXMLVal("/config/SMTPreport/TLSPort/text()");
     emailTLSPort = Integer.parseInt(TLSPort);
     SSLPort = getXMLVal("/config/SMTPreport/SSLPort/text()");
     emailSSLPort = Integer.parseInt(SSLPort);
     SMTPType = getXMLVal("/config/SMTPreport/SMTPType/text()");
     //ASB ...  Not in the form Encrypt3dpasswordvalueP4ssw0rd
     String decryptedEmailPassword = decrypt(sourceEmailPassword);
     if (decryptedEmailPassword.startsWith("Encrypt3d")&& decryptedEmailPassword.endsWith("P4ssw0rd")  ){
     //ASB ...  Extract the actual password
     //
     sourceEmailPassword = decryptedEmailPassword.substring(9, decryptedEmailPassword.length()-8);
     }else{
     //ASB ...  update the clear password
     String encryptedEmailPassword = "Encrypt3d" + sourceEmailPassword + "P4ssw0rd";
     encryptedEmailPassword = encrypt(encryptedEmailPassword);
     System.out.print(" ENCRYPTED eMail PASSWORD = " + encryptedEmailPassword);
     System.out.print(" ");
     // ASB ...  Write encrypted password back to the config.xml file
     String dirPath = System.getProperty("user.dir");
     String OS = System.getProperty("os.name").toLowerCase();
     String file = null;
     if (OS.indexOf("win") >= 0)  {
       file = dirPath + "\config\config.xml";
     } else { //Set to forward slash
       file = dirPath + "/config/config.xml";
     }
       // SET    file Name, root element, tag element, old value, new value
           updateXML(file,"SMTPreport","sourceEmailPassword", sourceEmailPassword, encryptedEmailPassword);
     }
     //ASB ...  Not in the form Encrypt3dpasswordvalueP4ssw0rd
     String decryptedExportCEPassword = decrypt(exportCEPassword);
     if (decryptedExportCEPassword.startsWith("Encrypt3d")&& decryptedExportCEPassword.endsWith("P4ssw0rd")  ){
         //ASB ...  Extract the actual password
         //
     exportCEPassword = decryptedExportCEPassword.substring(9, decryptedExportCEPassword.length()-8);
     }else{
     //ASB ...  update the clear password
     String encryptedExportCEPassword = "Encrypt3d" + exportCEPassword + "P4ssw0rd";
     encryptedExportCEPassword = encrypt(encryptedExportCEPassword);
     System.out.print(" ENCRYPTED CE Export PASSWORD = " + encryptedExportCEPassword);
     System.out.print(" ");
     // ASB ...  Write encrypted password back to the config.xml file
     String dirPath = System.getProperty("user.dir");
     String OS = System.getProperty("os.name").toLowerCase();
     String file = null;
     if (OS.indexOf("win") >= 0)  {
       file = dirPath + "\config\config.xml";
     } else { //Set to forward slash
       file = dirPath + "/config/config.xml";
     }
       // SET    file Name, root element, tag element, old value, new value
           updateXML(file,"ceexport","cepassword", exportCEPassword, encryptedExportCEPassword);
     }
     exportAuditFile = getXMLVal("/config/ceexport/AuditFile/text()");
     String tmp = getXMLVal("/config/ceimport/UpdatingBatchSize/text()");
     if (tmp.length() > 0){
          try {
            updatingBatchSize = Integer.parseInt(tmp);
          }
          catch (Exception e){
             updatingBatchSize = 250;
          }
     }
     else {
           updatingBatchSize = 250;
     }
     // import parameters
     importOSName = getXMLVal("/config/ceimport/osname/text()");
     //importOSRootFolder = getXMLVal("/config/ceimport/osrootfolder/text()");
     importCEUrl = getXMLVal("/config/ceimport/ceurl/text()");
     importCEStanza = getXMLVal("/config/ceimport/cestanza/text()");
     importCELoginConfig = getXMLVal("/config/ceimport/celoginconfig/text()");
     importCEUser = getXMLVal("/config/ceimport/ceuser/text()");
     importCEPassword = getXMLVal("/config/ceimport/cepassword/text()");
     //ASB ...  Check if this is still clear ie
     //ASB ...  Not in the form Encrypt3dpasswordvalueP4ssw0rd
     String decryptedImportCEPassword = decrypt(importCEPassword);
     if (decryptedImportCEPassword.startsWith("Encrypt3d")&& decryptedImportCEPassword.endsWith("P4ssw0rd")  ){
     //ASB ...  Extract the actual password
     //
       importCEPassword = decryptedImportCEPassword.substring(9, decryptedImportCEPassword.length()-8);
     }else{
          //ASB ...  update the clear password
     String encryptedImportCEPassword = "Encrypt3d" + importCEPassword + "P4ssw0rd";
     encryptedImportCEPassword = encrypt(encryptedImportCEPassword);
     System.out.print(" ENCRYPTED CE IMPORT PASSWORD = " + encryptedImportCEPassword);
     System.out.print(" ");
     // ASB ...  Write encrypted password back to the config.xml file
     String dirPath = System.getProperty("user.dir");
     String OS = System.getProperty("os.name").toLowerCase();
             String file = null;
          if (OS.indexOf("win") >= 0)  {
            file = dirPath + "\config\config.xml";
          } else { //Set to forward slash
            file = dirPath + "/config/config.xml";
          }
  // SET    file Name, root element, tag element, old value, new value
      updateXML(file,"ceimport","cepassword", importCEPassword, encryptedImportCEPassword);
     }
     //ASB ...  export parameters for Session Object
     exportCEUrl = getXMLVal("/config/ceexport/ceurl/text()");
     exportCEStanza = getXMLVal("/config/ceexport/cestanza/text()"); //null
     exportWaspLocation = getXMLVal("/config/ceexport/WaspLocation/text()"); //null
     exportCELoginConfig = getXMLVal("/config/ceexport/celoginconfig/text()");
     //ASB ...  Get Delete Document and Delete Folder parameters
     propCardinality =  getXMLVals("/config/AUDITProcessor/PropCardinalityList/PropCardinality/text()");
     propNames = getXMLVals("/config/AUDITProcessor/DocumentPropertyNames/PropertyName/text()");
     //propFlags ASB
     propFlags = getXMLVals("/config/AUDITProcessor/DocumentWhiteListFlags/PropFlag/text()");
     propTypes = getXMLVals("/config/AUDITProcessor/DocumentPropertyTypes/PropertyType/text()");
     }
         public void updateProcessDate(Date processDate){
         if (startSearchDate.length() == 0) {
              return;
         }
     // ASB ...  Write Date String back to the config.xml file
         // Need date in the format 20220823T125628Z
     String dirPath = System.getProperty("user.dir");
     String OS = System.getProperty("os.name").toLowerCase();
     String file = null;
     if (OS.indexOf("win") >= 0)  {
       file = dirPath + "\config\config.xml";
     } else { //Set to forward slash
       file = dirPath + "/config/config.xml";
     }
           SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
           //Need 24 Hour clock 00 -> 23 !! above is 12 hour
           SimpleDateFormat sdfTime = new SimpleDateFormat("HHmmss");
           String newStartSearchDate = sdf.format(processDate)+ "T" + sdfTime.format(processDate)+ "Z";
       updateXML(file,"AUDITProcessor","startsearchdate", startSearchDate, newStartSearchDate);
         }
         public void updateChoiceListFlag(String choiceListFlag){
              if (choiceListFlag.length() == 0) {
                   return;
              }
     // ASB ...  Write ChoiceListFlag String back to the config.xml file
     // Need to set the System path for the config.xml file dependent on the platform
     String dirPath = System.getProperty("user.dir");
     String OS = System.getProperty("os.name").toLowerCase();
        String file = null;
          if (OS.indexOf("win") >= 0)  {
            file = dirPath + "\config\config.xml";
          } else { //Set to forward slash
            file = dirPath + "/config/config.xml";
          }
          String newchoiceListFlag = "No";
               updateXML(file,"ChoiceListProcessor","SET_TO_GENERATE_CHOICELISTS", choiceListFlag, newchoiceListFlag);
         }
         private void getDocument() throws Exception {
          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
          DocumentBuilder db = null;
          try {
            db = dbf.newDocumentBuilder();
          } catch (ParserConfigurationException e) {
          logger.error(e);
          throw e;
     }
     //ASB ...  Normalise path to the config we want to use
     String dirPath = System.getProperty("user.dir");
     //ASB We can determine the Operatiing System (Windows v Linux) to set correct folder delimiters
     String OS = System.getProperty("os.name").toLowerCase();
     InputStream in = null;
     if (OS.indexOf("win") >= 0)  {
      in = new FileInputStream(dirPath + "\config\config.xml");
     } else { //Set to forward slash
      in = new FileInputStream(dirPath + "/config/config.xml");
     }
     dom = db.parse(in);
     in.close(); //ASB ...  Now close the config.xml stream
     }
     public String getXMLVal(String expression) throws Exception {
     Node n = (Node) xPath.evaluate(expression, dom, XPathConstants.NODE);
     if (n != null)
          return n.getNodeValue();
          return "";
     }
         //ASB ...  Put new value into the dom node
         private void updateXML(String file, String mainElement, String sTag, String strOldValue, String strNewValue) {
         Document doc = null;
         try {
              DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
              DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
              doc = docBuilder.parse(file);
              // Get the root element
              Node config = doc.getFirstChild();
              // Get the main element by tag name directly
              Node ceMain = doc.getElementsByTagName(mainElement).item(0);
              // loop the configuration main section child node
              NodeList list = ceMain.getChildNodes();
              for (int i = 0; i < list.getLength(); i++) {
              Node node = list.item(i);
            // get the password element, and update the value
                 if (sTag.equals(node.getNodeName())) {
                   node.setTextContent(strNewValue);
                   break;
                 }
              }
            } catch (ParserConfigurationException pce) {
              pce.printStackTrace();
             } catch (IOException ioe) {
            ioe.printStackTrace();
          } catch (SAXException sae) {
            sae.printStackTrace();
             }
             catch(Exception ex) {
                  String exError = ex.getMessage();
             }
             File filer = new File(file);
             saveToXML(filer,doc);
        }     // Save the updated DOM into the XML back
         // Save the updated DOM into the XML back
         private void saveToXML(File file, Document doc) {
             try {
                  TransformerFactory factory = TransformerFactory.newInstance();
                  Transformer transformer = factory.newTransformer();
                  transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                  StringWriter writer = new StringWriter();
                  StreamResult result = new StreamResult(writer);
                  DOMSource source = new DOMSource(doc);
                  transformer.transform(source, result);
                  String strTemp = writer.toString();
                  FileWriter fileWriter = new FileWriter(file);
                  BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
                  bufferedWriter.write(strTemp);
                  bufferedWriter.flush();
                  bufferedWriter.close();
             }
             catch(Exception ex) {
             }
        }
     public String[] getXMLVals(String expression) throws Exception {
          Vector vtmp = null;
          NodeList nl = null;
          vtmp = new Vector();
          nl = (NodeList) xPath.evaluate(expression, dom, XPathConstants.NODESET);
           for (int x = 0; x < nl.getLength(); x ++){
               vtmp.add(nl.item(x).getNodeValue());
          }
           return (String[])vtmp.toArray(new String[vtmp.size()]);
          }
          public String getImportAuditFile(){
               return importAuditFile;
          }
          public String getImportAuditFileFolders(){
               return importAuditFileFolder;
          }
          public String getExportOSName(){
               return exportOSName;
          }
          public String getExportConnectionPointName(){
               return exportConnectionPoint;
          }
          public String getExportCEUser(){
               return exportCEUser;
          }
          public String getExportCEPassword(){
               return exportCEPassword;
          }
     //ASB ... add getter Methods for export Session Object
          public String getExportCEUrl(){
               return exportCEUrl;
          }
          public String getExportCEStanza(){
               return exportCEStanza;
          }
          public String getExportWaspLocation(){
               return exportWaspLocation;
          }
          public String getExportCELoginConfig(){
               return exportCELoginConfig;
          }
          public Integer getmaximumDocs(){
               return maximumDocs;
          }
          public Integer getNoQQuestions(){
               return noQQuestions;
          }
          public Integer getNoQColumns(){
               return noQColumns;
          }
          public Integer getMaxSearchSize(){
               return maxSearchSize;  //ASB ...
          }
         //New Getters for the eMail configuration
          public String getSourceEmailAddress(){
               return sourceEmailAddress;  //ASB ...
          }
          public String getsourceEmailPassword(){
               return sourceEmailPassword;  //ASB ...
          }
          public String getSMTPTLSRequired(){
               return SMTPTLSRequired;  //ASB ...
          }
          public String getSMTPHost(){
               return SMTPHost;  //ASB ...
          }
          public String getSMTPEmailTemplatePath(){
               return SMTPEmailTemplates;  //ASB ...
          }
          public String getSMTPUser(){
               return SMTPUser;  //ASB ...
          }
          public String getSMTPSSLRequired(){
               return SMTPSSLRequired;  //ASB ...
          }
          public Integer getEmailTLSPort(){
               return emailTLSPort;  //ASB ...
          }
          public Integer getEmailSSLPort(){
               return emailSSLPort;  //ASB ...
          }
          public String getStringTLSPort(){
               return TLSPort;  //ASB ...
          }
          public String getStringSSLPort(){
               return SSLPort;  //ASB ...
          }
          public String getDebugOutputFlag(){
               return debugFlag;  //ASB ... 4 020
          }
          public String getLDAPSearchFlag(){
               return LDAPSearchFlag;  //ASB ... 4 027
          }
          public String getDeltaHours(){
               return deltaHours;  //ASB ...
          }
          public String getSTOPState(){ //
               return STOPState;  //ASB ... ChoiceListFlag
          }
          public String getChoiceListFlag(){ //
               return ChoiceListFlag;  //ASB ...
          }
          public Integer getSleepTime(){
               return SleepTime;  //ASB ...
          }
          public String getDeltaMinutes(){
               return deltaMinutes;  //ASB ...
          }
          public String getsearchProperty(){
               return searchProperty;  //ASB ...
          }
          public String getsearchPropertyDocs(){
               return searchPropertyDocs;  //ASB ...
          }
          public String getsearchPropertyFolders(){
               return searchPropertyFolders;  //ASB ...
          }
          public String getFolderSubclasses(){
               return folderCaseClasses;  //ASB ...
          }
          public String getJDBC(){
               return JDBC;               //ASB ...
          }
          public String getJDBCDriverClass(){
               return JDBCDriverClass;    //ASB ...
          }
          public String getJDBCUser(){
               return JDBCUser;           //ASB ...
          }
          public String getJDBCPassword(){
               return this.JDBCPassword;       //ASB ...
          }
          public String getEmailListName(){
               return EmailListName;       //ASB ...
          }
          public String getEmailFlagListName(){
               return EmailFlagListName;       //ASB ...
          }
          public String getStartDateraw(){
            if (startSearchDate.length() == 0){
                Date processDate = new Date();
               Date dateBefore = new Date(processDate.getTime() - processDate.getHours() * 3600*1000
          - processDate.getMinutes()*60*1000 - processDate.getSeconds()*1000 -60000); //Subtract n days
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
                //Need 24 Hour clock 00 -> 23 !! above is 12 hour
                SimpleDateFormat sdfTime = new SimpleDateFormat("HHmmss");
                    String newStartSearchDate = sdf.format(dateBefore)+ "T" + sdfTime.format(dateBefore)+ "Z";
          return newStartSearchDate; //ASB ...
          } else{
              return startSearchDate;
          }
          }
          public String getStartSearchDate(){
          //ASB allow search date to be set from the current system processing date
          if (startSearchDate.length() == 0){
               Date processDate = new Date();
               Date dateBefore = new Date(processDate.getTime() - processDate.getHours() * 3600*1000
               - processDate.getMinutes()*60*1000 - processDate.getSeconds()*1000 -60000); //Subtract n days
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
                //Need 24 Hour clock 00 -> 23 !! above is 12 hour
                SimpleDateFormat sdfTime = new SimpleDateFormat("HHmmss");
                String newStartSearchDate = sdf.format(dateBefore)+ "T" + sdfTime.format(dateBefore)+ "Z";
               return newStartSearchDate; //ASB ...
          }else{
            Date processDate = new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd",Locale.ENGLISH); //ASB ...  Added Locale
                Date javaStartDate = null;
                //Need 24 Hour clock 00 -> 23 !! hh is 12 hour
                SimpleDateFormat sdfTime = new SimpleDateFormat("HHmmss",Locale.ENGLISH); //ASB ...  Added Locale
     //ASB ..Check if the current date was manually set - ie is greater
     //      than one hour difference from the current time.
     //      If date was set manually then use this date instead as the search date.
              DateFormat diffDateFormat = DateFormat.getDateInstance();
              String sDiffDate = startSearchDate.substring(0, 8);
              String sDiffTime = startSearchDate.substring(9,15);
              Date diffDate = null;
              Date diffTime = null;
              Date dDeltaTime = null;
              Long dateSeconds = null;
              try {
                      //ASB ...  Need to subtract off the java start date
                   javaStartDate = sdf.parse("19700101"); //ASB ...
                   diffDate = sdf.parse(sDiffDate) ;
                   diffTime = sdfTime.parse(sDiffTime) ;
                   //Test for zero
                   Long searchhours = (long) 0;
                   if((diffTime.getTime() - javaStartDate.getTime() - Long.parseLong(deltaHours)*3600000)  > 0)
                  searchhours =(diffTime.getTime() - javaStartDate.getTime() - Long.parseLong(deltaHours)*3600000)/3600000;
                   String sSearchHours = searchhours.toString();
                   if (sSearchHours.length() == 1)sSearchHours = "0"+ sSearchHours;
                   dDeltaTime =  sdfTime.parse(sSearchHours + "0000");
             deltaSearchDate = sdf.format(processDate) + "T" + sSearchHours + "0000Z";
            //ASB ...  Added subtraction of the Java Start Date milliseconds
             dateSeconds = (diffDate.getTime() + (diffTime.getTime()) - (dDeltaTime.getTime()));
              } catch (ParseException e) {
          // Should be a controlled format - set to default if we get here
          e.printStackTrace();
          }
              //ASB ...  Get Process Date in milliseconds
              Long milliSecondsLastDate  = processDate.getTime();
              //ASB ...  Allow 30 Minute delta on top
              if ((milliSecondsLastDate - dateSeconds) < (diffTime.getTime()- javaStartDate.getTime() )){
                  startSearchDate = deltaSearchDate; //Allow 60 Minutes to fix missing documents
              }
               return startSearchDate; //ASB ...
               }
          }
          public String getQStartSearchDate(){
              //ASB ...  Now dynamically update the search date from the System Date
               //ASB ... 18-05-2022
              Date newDateSearch = new Date();
               SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd",Locale.ENGLISH);
               //Need 24 Hour clock 00 -> 23 !! above is 12 hour
              SimpleDateFormat sdfTime = new SimpleDateFormat("HHmmss",Locale.ENGLISH);
              // Need date in the format 20221805T152528Z
               int DeltaHours = Integer.parseInt(QSearchDays) * 24;
               String sDiffDate = sdf.format(newDateSearch);
               String sDiffTime = sdfTime.format(newDateSearch);
               String sSearchDate = sDiffDate + "T" + sDiffTime + "Z" ;
              SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
              try {
                     String sDate = sSearchDate.substring(0, 8);
                     String sTime = sSearchDate.substring(9,15);
                    newDateSearch = formatter.parse(sDate + sTime);
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(newDateSearch);
                    cal.add(Calendar.HOUR, -DeltaHours);
                    newDateSearch = cal.getTime();
              } catch (ParseException e) {
                      e.printStackTrace();
             }
     // Need date in the format 20220823T125628Z
            QstartSearchDate = sdf.format(newDateSearch)+ "T" + sdfTime.format(newDateSearch)+ "Z";
             return QstartSearchDate; //ASB ... 2 022
          }
          public String getExportedAuditFile(){
               return exportAuditFile;
          }
          public String getImportOSName(){
               return importOSName;
          }
          public String getImportOSRootFoler(){
               return importOSRootFolder;
          }
          public String getImportCEUrl(){
               return importCEUrl;
          }
          public String getImportCEStanza(){
               return importCEStanza;
          }
          public String getImportCELoginConfig(){
               return importCELoginConfig;
          }
          public String getImportCEUser(){
               return importCEUser;
          }
          public String getImportCEPassword(){
               return importCEPassword;
          }
          public Integer getImportMaxErrorCount(){
               return importMaxErrorCount;
          }
          public Integer getImportMaxDocCount(){
               return importMaxDocCount;
          }
          public String getQSearchDays(){ //QSearchDays
               return QSearchDays;
          }
          public String getQSelectList(){ //QSearchDays
               return QSelectList;  //
          }
          public String getQWhereClause(){
               return QWhereClause;  //
          }
          public String getAUDITORMainTask(){
               return AUDITORMainTask;  //
          }
          public String getQCaseFolderWhereClause(){
               return QCaseFolderWhereClause;  // ASB 11-08-2022
          }
          public String getQOrderByClause(){
               return QOrderByClause;  //
          }
          public String getQCaseFolderOrderByClause(){
               return QCaseFolderOrderByClause; // ASB 11-08-2022
          }
          public String getReportDirectoryName(){
               return ReportDirectoryName;
          }
          public String getTestFolderName(){
               return TestFolderName;
          }
          public String getDateInOracleFormat(String ISO8601Date){
     //ASB Return as ddmmyyyyhhmmss from     ISO 8601 format
     //ASB eg Change from  <upper_bound_date>20220723T041232Z</upper_bound_date>
             //              to                      yyyymmddhhmmss
     String OracleFormat = ISO8601Date.substring(6, 8) + ISO8601Date.substring(4, 6) + ISO8601Date.substring(0, 4) + ISO8601Date.substring(9, 15);
          return OracleFormat;
     }
         public void updateUpperBoundDate(Date newUpperBoundDate){
          // ASB ... 11-08-2022 Write Date String back to the config.xml file
              // Need date in the format 20220823T125628Z
              //ASB ... 11-08-2022 - Make platform independent
              String OS = System.getProperty("os.name").toLowerCase();
              String file;
              if (OS.indexOf("win") >= 0)  {
                   file = System.getProperty("user.dir") + "\config\config.xml";
              } else { //Set to forward slash
                   file = System.getProperty("user.dir") + "/config/config.xml";
              }
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
                //Need 24 Hour clock 00 -> 23 !! above is 12 hour
                SimpleDateFormat sdfTime = new SimpleDateFormat("HHmmss");
                String sNewUpperBoundDate = sdf.format(newUpperBoundDate)+ "T" + sdfTime.format(newUpperBoundDate)+ "Z";
       updateXML(file,"config","upper_bound_date", startSearchDate, sNewUpperBoundDate);
         }
          public int getUpdatingBatchSize(){
               return updatingBatchSize;
          }
          public Integer getImportMaxRunTimeMinutes(){
               return importMaxRunTimeMinutes;
          }          public String getPropName(int i){
               return propNames[i];
          }
          public int getPropType(int i){
               return Integer.parseInt(propTypes[i]);
          }
          public int getPropCardinality(int i){
               return Integer.parseInt(propCardinality[i]);
          }
          //ASB IBM -  Get Boolean Property Flag name or "NONE"
          public String getPropFlags(int i){
               return propFlags[i];
          }
          public String get_DocPathName(){
               return docPathName;
          }
          public String get_CEPathName(){
               return CEFolderPath;
          }
          //getDocClassName()
          public String getDocClassName(){
               return DocClassName;
          }
          //ASB Flag set to YES will search the subclasses 11-08-2022
          public String getSearchSubClasses(){
               return SearchSubClasses;
          }
          public String getReportPath(){
               return ReportPath;
          }
          public String getReportExtension(){
               return ReportExtension;
          }
          //getCaseStatus
          public String getCaseStatus(){
               return caseStatus;
          }
          //getCaseStatusField
          public String getCaseStatusField(){
               return caseStatusField;
          }
          //getMimeType
          public String getMimeType(){
               return MimeType;
          }
          //getSolutionPrefix
          public String getSolutionPrefix(){
               return SolutionPrefix;
          }
           private static String encrypt(String str) {
                  try {
                      //encoding  byte array into base 64
                      byte[] encoded = Base64.encodeBase64(str.getBytes());
                      String encString = new String(encoded);
                      return encString;
                  } catch (Exception e) {
                  }
                  return null;
              }
              private static String decrypt(String str) {
                  try {
                      //decoding byte array into base64
                      byte[] decoded = Base64.decodeBase64(str);
                      String sDecoded =  new String(decoded);
               return sDecoded;
                  } catch (Exception e) {
                  }
                  return null;
              }
     }
Listing 6-12

The AUDITReportConfig.java code for reading the config.xml file

Creating the com.asb.config Package

A cropped screenshot of a page displays a list under the audit report, where s r c is selected, lists a menu, where new is selected, and further package is selected.

Figure 6-10

The second Java package is added

A config package, named com.asb.config, is created as follows.

A screenshot of a new java package window. It displays the options to create a new java package, where the name is given as, com dot a s b dot config, and the finish button is selected.

Figure 6-11

The com.asb.config package is added to Audit_Report/src

Creating the ConfigVal Java Class

A cropped screenshot of a page displays a list under the audit report, where com dot a s b dot config is selected, lists a menu, where new is selected, and further class is selected.

Figure 6-12

A New Java Class is added to the com.asb.config package

The ConfigVal Class is added to the com.asb.config package.

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, config val, and the finish button is selected.

Figure 6-13

The ConfigVal class is created under com.asb.config

The Java code for the ConfigVal.java source is as follows.
package com.asb.config;
public class ConfigVal {
     public String setting;
     public String default_val;
     public ConfigVal(String setting, String val){
          this.setting = setting;
          this.default_val = val;
     }
}
Listing 6-13

The Java code for the ConfigVal.java file

This Java code has a ConfigVal public method, used as an initial test code for getter and setter methods. (It is not currently used in the Audit_Report program.)

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, mime type, and the finish button is selected.

Figure 6-14

The MimeType Java class is created under com.asb.config

Adding the MimeType Java Code

The Java code for the MimeType.java source is as follows.
package com.asb.config;
public class MimeType {
     private String mimeString;
     private String extension;
     public void setMimeString(String mimeString) {
          this.mimeString = mimeString;
     }
     public String getMimeString() {
          return mimeString;
     }
     public void setExtension(String extension) {
          this.extension = extension;
     }
     public String getExtension() {
          return extension;
     }
}
Listing 6-14

The MimeType.java code file

This Java code has a MimeType public method, used as initial test code for getter and setter methods. (It is not currently used in the Audit_Report program.)

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, audit config, and the finish button is selected.

Figure 6-15

The AUDITConfig class is created under com.asb.config

Adding the AUDITConfig Java Code

The Java code for the AUDITConfig.java source is as follows.
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions. IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you "AS IS "
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
/**
 *
 */
/**
 * @author Alan S. Bluck, ASB Software Development Limited
 *
 */
package com.asb.config;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Vector;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.log4j.Logger;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class AUDITConfig extends XMLConfigReader {
     static Logger logger = Logger.getLogger(AUDITConfig.class.getName());
     private static AUDITConfig theInstance;
     private static HashMap mimeTypes;
     static {
          if (theInstance == null)
               try {
                    theInstance = new AUDITConfig();
               } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
               }
     }
     public static String getStrVal(ConfigVal cv){
          return AUDITConfig.getConfigStringValue(cv.setting, cv.default_val);
     }
     public static int getIntVal(ConfigVal cv){
          return AUDITConfig.getConfigIntValue(cv.setting, cv.default_val);
     }
     public static int getConfigIntValue(String expression, String sdefault) {
          try {
               return Integer.parseInt(theInstance.get_ConfigValue(expression, sdefault));
          } catch (Exception e) {
               logger.warn(e.getMessage(), e);
               return Integer.parseInt(sdefault);
          }
     }
     public static String getConfigStringValue(String expression, String sdefault) {
          try {
               return theInstance.get_ConfigValue(expression, sdefault);
          } catch (Exception e) {
               logger.warn(e.getMessage(), e);
               return sdefault;
          }
     }
     public AUDITConfig() throws Exception {
          XPathFactory  factory= XPathFactory.newInstance();
          String fullPath = System.getProperty("user.dir") + File.separator + "config" + File.separator + "config.xml";
          logger.debug("AUDITConfig reading " + fullPath);
          setConfigXml(fullPath, true);
          NodeList nlMimeTypes = getNodeList("//config/mimetypes/*");
          theInstance.mimeTypes = new HashMap();
          for (int x = 0; x < nlMimeTypes.getLength(); x++){
               MimeType mt = new MimeType();
               Node mtn = nlMimeTypes.item(x);
               String mimeString = getConfigValue(mtn,"mimestring");
               mt.setMimeString(mimeString);
               String ext = getConfigValue(mtn,"extension");
               if (!ext.startsWith("."))
                    ext = "." + ext;
               mt.setExtension(ext);
               theInstance.mimeTypes.put(mimeString, mt);
          }
     }
     public static Node getConfigNode(String expression) throws Exception {
          return theInstance.getNode(expression);
     }
     public static String getNodeStringValue(String expression,Node n) throws XPathExpressionException{
          return theInstance.get_NodeStringValue(expression, n);
     }
     public static HashMap getMimeTypes() throws Exception {
          return theInstance.mimeTypes;
     }
     public static String[] getXMLVals(String expression, Node n) throws Exception {
          return theInstance.get_XMLVals(expression, n);
     }
}
Listing 6-15

The AuditConfig.java code file

The Java class, AUDITConfig, is an example of the object-oriented features of the Java language, as this class extends the abstract XMLConfigReader class (see the following Java code). This class is used to read the XML tag elements of the config.xml file we use for the Audit Report Java program.

Note

An abstract class, like XMLConfigReader, allows the method functionality to be created that the subclass can implement or override; however, the subclass (AUDITConfig in our implementation) can extend only one abstract class.

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, X M L config reader, and the finish button is selected.

Figure 6-16

The abstract class, XMLConfigReader, is created

The code for this Java class is used by the AUDITConfig class defined in the following line of code:
public class AUDITConfig extends XMLConfigReader {
The Java code for the XMLConfigReader.java source is as follows.
package com.asb.config;
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions. IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you "AS IS "
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Vector;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public abstract class XMLConfigReader {
     private static Logger logger = Logger.getLogger(XMLConfigReader.class.getName());
     private XPath xPath;
     private String xmlConfig;
     private Document dom;
     private String fullPath;
     protected String get_NodeStringValue(String expression,Node n) throws XPathExpressionException{
          return (String)xPath.evaluate(expression, n, XPathConstants.STRING);
     }
     protected Node getNode(String expression) throws Exception {
          return (Node)xPath.evaluate(expression, dom, XPathConstants.NODE);
     }
     protected NodeList getNodeList(String expression) throws XPathExpressionException{
          return (NodeList) xPath.evaluate(expression, dom, XPathConstants.NODESET);
     }
     protected void setConfigXml(String fileName, boolean fullPath){
          XPathFactory  factory= XPathFactory.newInstance();
          xPath=factory.newXPath();
          xPath.reset();
          if (fullPath)
               this.fullPath = fileName;
          xmlConfig = fileName;
          try {
               getDocument(fullPath);
          } catch (Exception e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
          }
     }
     public String getConfigValue(String expression) throws Exception {
          return get_ConfigValue(expression,null);
     }
     public String getConfigValue(Node n,String expression) throws Exception {
          return getConfigValue(n,expression,null);
     }
     public void setConfigValue(String expression,String val){
          expression = expression.replace(".", "/");
          Node n1 = null;
          try {
               n1 = (Node) xPath.evaluate(expression, dom, XPathConstants.NODE);
          } catch (XPathExpressionException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
          }
          if (n1 != null)
               n1.setTextContent(val);
     }
     public String get_ConfigValue(String expression,String sdefault) throws Exception {
          expression = expression.replace(".", "/");
          expression = "//" + expression + "/text()";
          Node n1 = (Node) xPath.evaluate(expression, dom, XPathConstants.NODE);
          if (n1 == null)
               return sdefault;
          return n1.getNodeValue();
     }
     public String getConfigValue(Node n, String expression, String sdefault) throws Exception {
          expression = expression.replace(".", "/");
          expression = expression + "/text()";
          Node n1 = (Node) xPath.evaluate(expression, n, XPathConstants.NODE);
          if (n1 == null)
               return sdefault;
          return n1.getNodeValue();
     }
     private void getDocument(boolean fullPath) throws Exception {
          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
          DocumentBuilder db = null;
          try {
               db = dbf.newDocumentBuilder();
          } catch (ParserConfigurationException e) {
               logger.error(e);
               throw e;
          }
          InputStream in;
          if (fullPath){
               in = new FileInputStream(xmlConfig);
          }
          else {
               in = XMLConfigReader.class.getClassLoader().getResourceAsStream(xmlConfig);
          }
          dom = db.parse(in);
     }
     protected String getXMLStringVal(String expression) throws Exception {
          Node n = (Node) xPath.evaluate(expression, dom, XPathConstants.NODE);
          if (n != null)
               return n.getNodeValue();
          return "";
     }
     protected void setXMLStringVal(String expression, String val) throws Exception {
          Node n = (Node) xPath.evaluate(expression, dom, XPathConstants.NODE);
          if (n != null)
               n.setNodeValue(val);
     }
     protected int getXMLIntVal(String expression) throws Exception {
          Node n = (Node) xPath.evaluate(expression, dom, XPathConstants.NODE);
          if (n != null){
               String test = n.getNodeValue();
               return Integer.parseInt(n.getNodeValue());
          }
          return -1;
     }
     public String[] get_XMLVals(String expression, Node n) throws Exception {
          Vector vtmp = null;
          NodeList nl = null;
          vtmp = new Vector();
          nl = (NodeList) xPath.evaluate(expression, n, XPathConstants.NODESET);
          for (int x = 0; x < nl.getLength(); x ++){
               vtmp.add(nl.item(x).getNodeValue());
          }
          return (String[])vtmp.toArray(new String[vtmp.size()]);
     }
     public String[] get_XMLVals(String expression) throws Exception {
          Vector vtmp = null;
          NodeList nl = null;
          vtmp = new Vector();
          nl = (NodeList) xPath.evaluate(expression, dom, XPathConstants.NODESET);
          for (int x = 0; x < nl.getLength(); x ++){
               vtmp.add(nl.item(x).getNodeValue());
          }
          return (String[])vtmp.toArray(new String[vtmp.size()]);
     }
     public void save() throws Exception {
          if (fullPath != null){
               logger.debug("XMLConfigReader saving " + fullPath);
            Source source = new DOMSource(dom);
             // Prepare the output file
             File file = new File(fullPath);
             Result result = new StreamResult(file);
             // Write the DOM document to the file
             Transformer xformer = TransformerFactory.newInstance().newTransformer();
             xformer.transform(source, result);
          }
     }
}
Listing 6-16

The XMLConfigReader.java source code file

Adding the com.ibm.filenet.ps.ciops Package

A cropped screenshot of a page displays a list under the audit report, where s r c is selected, lists a menu, where new is selected, and further package is selected.

Figure 6-17

A new package option for com.ibm.filenet.ps.ciops is selected

This is a base code package for a FileNet Component Integration Java code.

A screenshot of a new java package window. It displays the options to create a new java package, where the name is given as com dot i b m dot file net dot p s dot c i o p s.

Figure 6-18

The package name com.ibm.filenet.ps.ciops is entered

Adding the ContentElementDataSource Java Class

The ContentElementDataSource Java class is created under the new com.ibm.filenet.ps.ciops Java package as follows.

A cropped screenshot of a page displays a list under the audit report, where com dot i b m dot file net dot p s dot c i o p s is selected, lists a menu, where new is selected, and further class is selected.

Figure 6-19

The New ContentElementDataSource class is created

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, content element data source, and the finish button is selected.

Figure 6-20

The ContentElementDataSource Class name is entered

The Java code for the ContentElementDataSource.java source is as follows.
package com.ibm.filenet.ps.ciops;
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions.  IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you "AS IS"
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.activation.DataSource;
import com.filenet.api.core.ContentTransfer;
/**
 * This class wraps a ContentTransfer object in a DataSource object. This way
 * the content can be used as a part of an e-mail message.
 *
 * @author Ricardo Belfor
 *
 */
public class ContentElementDataSource implements DataSource {
     private ContentTransfer contentTransfer;
     public ContentElementDataSource(ContentTransfer contentTransfer) {
          this.contentTransfer = contentTransfer;
     }
     public String getContentType() {
          return contentTransfer.get_ContentType();
     }
     public InputStream getInputStream() throws IOException {
          return contentTransfer.accessContentStream();
     }
     public String getName() {
          return contentTransfer.get_RetrievalName();
     }
     public OutputStream getOutputStream() throws IOException {
          return null;
     }
}
Listing 6-17

The ContentElementDataSource.java source code file

The preceding code supports the email sendmessage Java code which is used as an example IBM FileNet Component Integration Workflow step call.

Adding the QOperations Java Class

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, Q operations, and the finish button is selected.

Figure 6-21

The QOperations class code is added to the package

The Java code for the QOperations.java source is as follows.
package com.ibm.filenet.ps.ciops;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
import javax.security.auth.Subject;
import org.apache.commons.io.LineIterator;
import com.asb.ce.utils.AUDITReportConfig;
import com.asb.config.AUDITConfig;
import com.filenet.api.admin.ClassDefinition;
import com.filenet.api.admin.LocalizedString;
import com.filenet.api.admin.PropertyDefinition;
import com.filenet.api.collection.AccessPermissionList;
import com.filenet.api.collection.ChoiceListSet;
import com.filenet.api.collection.ContentElementList;
import com.filenet.api.collection.DocumentSet;
import com.filenet.api.collection.FolderSet;
import com.filenet.api.collection.IndependentObjectSet;
import com.filenet.api.collection.PropertyDefinitionList;
import com.filenet.api.collection.StringList;
import com.filenet.api.collection.VersionableSet;
import com.filenet.api.constants.AutoClassify;
import com.filenet.api.constants.AutoUniqueName;
import com.filenet.api.constants.CheckinType;
import com.filenet.api.constants.ChoiceType;
import com.filenet.api.constants.DefineSecurityParentage;
import com.filenet.api.constants.FilteredPropertyType;
import com.filenet.api.constants.PropertyNames;
import com.filenet.api.constants.RefreshMode;
import com.filenet.api.constants.ReservationType;
import com.filenet.api.constants.TypeID;
import com.filenet.api.constants.VersionStatus;
import com.filenet.api.core.Connection;
import com.filenet.api.core.Document;
import com.filenet.api.core.Domain;
import com.filenet.api.core.EntireNetwork;
import com.filenet.api.core.Factory;
import com.filenet.api.core.Factory.Choice;
import com.filenet.api.core.Factory.ChoiceList;
import com.filenet.api.core.Factory.ContentTransfer;
import com.filenet.api.core.Factory.ReferentialContainmentRelationship;
import com.filenet.api.core.Factory.VersionSeries;
import com.filenet.api.core.Folder;
import com.filenet.api.core.CmTask; //ASB added for Task property retrieval
import com.filenet.api.core.IndependentObject;
import com.filenet.api.core.ObjectStore;
import com.filenet.api.core.UpdatingBatch;
import com.filenet.api.exception.EngineRuntimeException;
import com.filenet.api.exception.ExceptionCode;
import com.filenet.api.property.FilterElement;
import com.filenet.api.property.PropertyFilter;
import com.filenet.api.query.SearchSQL;
import com.filenet.api.query.SearchScope;
import com.filenet.api.util.Id;
import com.ibm.filenet.ps.ciops.database.DatabasePrincipal;
import filenet.vw.api.VWAttachment;
import filenet.vw.api.VWAttachmentType;
import filenet.vw.api.VWException;
import filenet.vw.api.VWLibraryType;
import filenet.vw.api.VWSession;
import filenet.vw.base.logging.Logger;
import java.io.File;
//Q PDF Creation Operations and supporting methods 29th July 2022
import java.io.FileOutputStream;
import java.util.Date;
import java.util.HashMap;
import com.lowagie.text.Anchor;
import com.lowagie.text.BadElementException;
//import com.lowagie.text.BaseColor;
import com.lowagie.text.Chapter;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Image;
import com.lowagie.text.List;
import com.lowagie.text.ListItem;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;
import com.lowagie.text.Section;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.pdf.RandomAccessFileOrArray;
import com.lowagie.text.pdf.codec.TiffImage;
//
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Vector;
import org.apache.commons.io.FileUtils;
//import org.apache.commons.lang.math.RandomUtils; //ASB removed this import 26-08-2022
//
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions. IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you "AS IS"
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
/**
 * Alan S.Bluck  29th July 2022
 * Code for QOperations
 * Adapted from:
 * Custom Java component serving as an example for the article
 * "Developing Java components for the Component Integrator".
 *
 * @author Ricardo Belfor
 *
 */
public class QOperations {
     private static Logger logger = Logger.getLogger( QOperations.class );
     private static final Properties QQidMap = new Properties();
     private static final int CardinalityENUM = 1;
     private static final int CardinalityLIST = 2;
     private static final int CardinalitySINGLE = 0;
     //ASB ... Add static types for property type recognition
          private static final int BinaryType = 1;
          private static final int BoolType = 2;
          private static final int DateType = 3;
          private static final int DoubleType = 4;
          private static final int GUIDType =     5;
          private static final int LongType = 6;
          private static final int ObjectType = 7;
          private static final int StringType = 8;
          private static AUDITReportConfig cemc;
          private static Connection connection;
          private static int processedCount;
          private ObjectStore os;
        private int maxMin;
          private UpdatingBatch ub;
          private HashMap classNames;
          //PDF Column and Table Section properties
          public static String [] ColumnNameT4 = new String[3]; //ASB Array of the Q Table 4 Columns
          public static String [] ColumnWidthT4 = new String[3]; //ASB Array of the Q Table 4 Columns
          public static String [] TableParams = new String[10]; //ASB Array of the Case task type parameters
        //AUDITOR Tab driving arrays
          public static String [] TableParams_AUDITOR = new String[10]; //ASB Array of the Case task type parameters
          public static String [] TabSECTIONS_AUDITOR = new String[10];
          public static String [][] AUDITOR_props = new String[10][50];
        //Audit Tab driving arrays
          public static String [] TableParams_Audit = new String[10]; //ASB Array of the Case task type parameters
          public static String [] TabSECTIONS_Audit = new String[10];
          public static String [][] Audit_props = new String[10][50];
        //Department Tab driving arrays
          public static String [] TableParams_Department = new String[10]; //ASB Array of the Case task type parameters
          public static String [] TabSECTIONS_Department = new String[10];
          public static String [][] Department_props = new String[10][50];
          //PDF Fonts required
          private static Font catFont = new Font(Font.TIMES_ROMAN, 18,
                     Font.BOLD);
          private static Font redFont = new Font(Font.TIMES_ROMAN, 12,
                     Font.NORMAL);
          private static Font subFont = new Font(Font.TIMES_ROMAN, 16,
                     Font.BOLD);
                 private static Font smallBold = new Font(Font.TIMES_ROMAN, 12,
                     Font.BOLD);
         private static String sTab = "u0009";
     public QOperations() {
          try {
               cemc = new AUDITReportConfig();
          } catch (Exception e1) {
               // TODO Auto-generated catch block
               e1.printStackTrace();
          } //ASB add our config.xml file methods here!
          String fullPath = System.getProperty("user.dir") + File.separator
                    + "config" + File.separator + "QQID.properties";
          try {
               QQidMap.load(new FileInputStream(new File(fullPath)));
               return;
          } catch (FileNotFoundException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
          } catch (IOException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
          }
          throw new RuntimeException(fullPath + " cannot load QQid map");
          //java.sql.Connection databaseConnection = getDatabaseConnection();
          //if ( databaseConnection != null ) {
          //     logger.debug( databaseConnection.toString() );
          //} else {
          //     logger.debug( "No database connection" );
          //}
     }
     protected Connection getConnection() {
          String uri = System.getProperty("filenet.pe.bootstrap.ceuri");
          return Factory.Connection.getConnection(uri);
     }
     protected VWSession getVWSession() throws VWException {
          String connectionPoint = System.getProperty("filenet.pe.cm.connectionPoint");
          return new VWSession(connectionPoint);
     }
     protected java.sql.Connection getDatabaseConnection() {
          Subject subject = Subject.getSubject( AccessController.getContext() );
          Set<DatabasePrincipal> principals = subject.getPrincipals( DatabasePrincipal.class );
          if ( principals != null && ! principals.isEmpty() ) {
               DatabasePrincipal principal = principals.iterator().next();
               return principal.getConnection();
          }
          return null;
     }
     private Folder getFolderFromAttachment(VWAttachment folderAttachment) {
          ObjectStore objectStore = getObjectStore( folderAttachment.getLibraryName() );
          Folder folder = (Folder) objectStore.getObject("Folder", folderAttachment.getId() );
          return folder;
     }
     private Document getDocumentFromAttachment(VWAttachment documentAttachment) {
          ObjectStore objectStore = getObjectStore( documentAttachment.getLibraryName() );
          Document folder = (Document) objectStore.getObject("Document", documentAttachment.getId() );
          return folder;
     }
     public ObjectStore getObjectStore( String objectStoreName ) {
          Connection connection = getConnection();
          EntireNetwork entireNetwork = Factory.EntireNetwork.fetchInstance(connection, null);
          Domain domain = entireNetwork.get_LocalDomain();
         String OSName = cemc.getExportOSName().trim();
          //os = Factory.ObjectStore.getInstance( domain, objectStoreName ); //Initial version
          os = Factory.ObjectStore.fetchInstance( domain, objectStoreName, null );   //ASB 8th August 2022
          return os;
     }
     /**
      * Returns the documents filed in the folder.
      *
      * @param folderAttachment the input folder.
      * @return an array of documents filed in the folder.
      * @throws Exception
      */
     public VWAttachment[] getFolderDocuments(VWAttachment folderAttachment ) throws Exception {
          Folder folder = getFolderFromAttachment(folderAttachment);
          DocumentSet containedDocuments = getContainedDocuments(folder);
          Iterator<?> iterator = containedDocuments.iterator();
          ArrayList<VWAttachment> containedDocumentList = new ArrayList<VWAttachment>();
          while ( iterator.hasNext() ) {
               com.filenet.api.core.Document document = (com.filenet.api.core.Document) iterator.next();
               VWAttachment documentAttachment = getAsVWAttachment(document);
               containedDocumentList.add( documentAttachment );
          }
          return containedDocumentList.toArray( new VWAttachment[0] );
     }
     private DocumentSet getContainedDocuments(Folder folder) {
          PropertyFilter propertyFilter = getContainedDocumentsPropertyFilter();
          folder.fetchProperties( propertyFilter );
          DocumentSet containedDocuments = folder.get_ContainedDocuments();
          return containedDocuments;
     }
     private PropertyFilter getContainedDocumentsPropertyFilter() {
          PropertyFilter propertyFilter = new PropertyFilter();
          propertyFilter.addIncludeProperty( new FilterElement( null, null, null, PropertyNames.CONTAINED_DOCUMENTS, null ) );
          propertyFilter.addIncludeProperty( new FilterElement( 2, null, null, PropertyNames.ID, null ) );
          propertyFilter.addIncludeProperty( new FilterElement( 2, null, null, "DocumentTitle", null ) );
          return propertyFilter;
     }
     private VWAttachment getAsVWAttachment(Document document) throws VWException {
          VWAttachment documentAttachment = new VWAttachment();
          documentAttachment.setLibraryType( VWLibraryType.LIBRARY_TYPE_CONTENT_ENGINE );
          ObjectStore objectStore = document.getObjectStore();
          objectStore.fetchProperties( new String[] { PropertyNames.NAME } );
          documentAttachment.setLibraryName( objectStore.get_Name() );
          document.fetchProperties( new String[] { PropertyNames.ID, PropertyNames.NAME } );
          documentAttachment.setId( document.get_Id().toString() );
          documentAttachment.setAttachmentName( document.get_Name() );
          documentAttachment.setType( VWAttachmentType.ATTACHMENT_TYPE_FOLDER );
          return documentAttachment;
     }
     /**
      * Sends an e-mail message using the mail session of the process engine
      * session. It uses a document stored in the Content Engine as a template.
      * The first content element is considered the main part of the template,
      * containing place holders for template values. Template value place
      * holders are prefixed in the template with a $-sign.
      * <p>
      * It may also contain references to images contained in the other content
      * elements of the document. The references have the following form:
      * <code>cid:contentX</code>, where <code>X</code> stands for the index of
      * the content element. So a reference to the first content element after the
      * main template will look like <code>cid:content1</code>. This reference
      * can be used in an HTML image tag as follows:<br/>
      * <code>&lt;img src='cid:content1'&gt;</code><br/>
      * The samples folder contains a example template with a background image
      * and an embedded logo.
      *
      * @param templateAttachment the template document
      * @param from
      *            from address.
      * @param to
      *            to address.
      * @param subject
      *            the subject of the message.
      * @param templateNames
      *            the name of the template variables.
      * @param templateValues
      *            the values of the template variables.
      *
      * @throws Exception
      */
     public void sendMailMessage(VWAttachment templateAttachment, String from, String to, String subject, String[] templateNames, String templateValues[] ) throws Exception {
          Document template = getDocumentFromAttachment(templateAttachment);
          EmailTemplate emailTemplate = new EmailTemplate(template);
          Session session = getVWSession().createMailSession();
          MimeMessage message = emailTemplate.getMessage(session, new InternetAddress(from), InternetAddress.parse( to ), subject, templateNames, templateValues );
          Transport.send(message);
     }
     public void writePDFToCaseFolder(String CaseGUID, String TaskGUID, String TaskName, String pdfFileName, String WorkingDirectory, String sQuestions,String sQuestionColumns, String StartQNo, String EndQNo) {
        //ASB takes the generated pdf File and imports it to a given Case Folder
          try {
               // Get Contained Documents in the Case Folder
             Folder caseFolder =fetchFolderById( os, CaseGUID);
               DocumentSet containedDocuments = getContainedDocuments(caseFolder);
               Iterator<?> iterator = containedDocuments.iterator();
               String [] docTitles = new String[100]; //TODO set this as a config.xml parameter
               com.filenet.api.core.Document [] docList = new com.filenet.api.core.Document[100];
               int nDocs = 0;
               while ( iterator.hasNext() ) {
                    com.filenet.api.core.Document document = (com.filenet.api.core.Document) iterator.next();
                    docTitles[nDocs] = document.get_Name();
                    docList[nDocs] = document;
                    nDocs ++;
               }
               importDocuments(CaseGUID,TaskGUID,TaskName,pdfFileName,WorkingDirectory,docTitles,docList,nDocs);
          } catch (Exception e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
          }
     }
     public String generatePDF(String CaseGUID, String TaskGUID, String TaskName, String pdfFileName, String WorkingDirectory, String sQuestions,String sQuestionColumns, String StartQNo, String EndQNo) throws IOException {
          // Call the pdf Builder method
          int nQuestions = Integer.parseInt(sQuestions);
          int questionColumns = Integer.parseInt(sQuestionColumns);
          int nStartQNO = Integer.parseInt(StartQNo);
          int nEndQNO = Integer.parseInt(EndQNo);
          String FileCreated = createPDFFile(CaseGUID, TaskGUID, TaskName, pdfFileName, WorkingDirectory, nQuestions+1,questionColumns,nStartQNO,nEndQNO);
        return FileCreated;
     }
     private String  createPDFFile(String CaseGUID, String TaskGUID, String TaskName, String pdfFileName, String WorkingDirectory,int nQuestions,int nQuestionColumns, int nStartQNO,int nEndQNO) throws IOException{
        //TODO Detect Linux or Windows environment and set paths accordingly
          String MainAUDITORTask =cemc.getAUDITORMainTask();
          File fTextFile = new File( pdfFileName);
          LineIterator lineIterator = null;
          String contentDirectoryName = WorkingDirectory;
          //File.separator
          String directoryName = contentDirectoryName +  File.separator + TaskName +  File.separator;
          String     txtFile = directoryName +  pdfFileName;
          fTextFile = new File(txtFile);
          //Create pdf File from text fileName
          String  pdfFile = fTextFile.getName().substring(0, fTextFile.getName().lastIndexOf('.') + 1) + "pdf";
        //Add the TaskID to give a more unique file name
          pdfFile = directoryName + TaskGUID + pdfFile;
          // TODO Auto-generated method stub
          //Document document = new Document();
          com.lowagie.text.Document document = new com.lowagie.text.Document(PageSize.A3.rotate(), 50, 50, 100, 100);
          String status = "pdf_create_started";
          String sTab = "u0009";
          String [][] AUDITOR_propValues;
          String [][] AUDITOR_propNames;
          String [][] Department_propValues;
          String [][] Department_propNames;
          String [][] Audit_propValues;
          String [][] Audit_propNames;
          try {
               //Set the PDF parameters required
               PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pdfFile));
               writer.setPdfVersion(PdfWriter.VERSION_1_3);
               //PdfWriter.getInstance(document, new FileOutputStream(pdfFile));
                document.open();
               //document.add(new Chunk(""));
                //TODO Get values from config.xml
               document.setMargins(50, 50, 100, 100);
               document.addTitle(pdfFileName);
               if(TaskName.equalsIgnoreCase(MainAUDITORTask)) { //ASB 22-08-2022 Read from config.xml
                    document.addSubject("AUDITOR Document");
               }else {
                   document.addSubject("Q Document");
               }
               document.addKeywords("AUDITOR, Q, Audit, Department");
               document.addAuthor("ASB Software Development Limited");
               document.addCreator("createPDFFile");
               //Retrieve Tabs 1 to 5 Q Case Fields (Audit)
               if(TaskName.equalsIgnoreCase(MainAUDITORTask)) {
                    addAUDITORTitlePage(document);
               }else {
                    addQTitlePage(document);
               }
               //============P D Q =============================
               Integer paramCount = 0;
               Integer fieldCount = 0; //set for loading Fields
               String fieldVal="";
               int nProps = 0;
               Integer [] noAUDITORprops = new Integer[10];
               //ASB  Retrieve the Tab Name parameters from the config.xml
               for (String field:TabSECTIONS_AUDITOR){
                    //ASB Returns NotUsed string for any elements not in the config.xml file
                    fieldVal = AUDITConfig.getConfigStringValue("config.TabSECTIONS_AUDITOR.T" + String.valueOf(fieldCount), "NotUsed");
                     if (!fieldVal.equalsIgnoreCase("NotUsed")){
                          TableParams_AUDITOR = fieldVal.split(",");
                          TabSECTIONS_AUDITOR[paramCount] = TableParams_AUDITOR[0];
                          nProps = Integer.parseInt(TableParams_AUDITOR[1]);
                          noAUDITORprops[paramCount] = nProps;
                          for (int i = 0; i < nProps; i++) {
                              AUDITOR_props[paramCount][i] = TableParams_AUDITOR[i+2];
                         }
                         paramCount++;
                     }
                    fieldCount++;
               }
               Integer noAUDITORSections = paramCount;
               fieldCount = 0;
               //===============================================
               paramCount = 0;
               fieldCount = 0; //set for loading Fields
               fieldVal="";
               nProps = 0;
               Integer [] noAuditprops = new Integer[10];
               //ASB  Retrieve the Tab Name parameters from the config.xml
               for (String field:TabSECTIONS_Audit){
                    //ASB Returns NotUsed string for any elements not in the config.xml file
                    fieldVal = AUDITConfig.getConfigStringValue("config.TabSECTIONS_Audit.T" + String.valueOf(fieldCount), "NotUsed");
                     if (!fieldVal.equalsIgnoreCase("NotUsed")){
                          TableParams_Audit = fieldVal.split(",");
                          TabSECTIONS_Audit[paramCount] = TableParams_Audit[0];
                          nProps = Integer.parseInt(TableParams_Audit[1]);
                          noAuditprops[paramCount] = nProps;
                          for (int i = 0; i < nProps; i++) {
                              Audit_props[paramCount][i] = TableParams_Audit[i+2];
                         }
                         paramCount++;
                     }
                    fieldCount++;
               }
               Integer noAuditSections = paramCount;
               fieldCount = 0;
               //Retrieve Tabs 1 to 5 Q Case Fields (Department)
               paramCount = 0;
               fieldCount = 0; //set for loading Fields
               fieldVal="";
               Integer [] noDepartmentprops = new Integer[10];
               //ASB  Retrieve the Tab Name parameters from the config.xml
               for (String field:TabSECTIONS_Department){
                    //ASB Returns NotUsed string for any elements not in the config.xml file
                    fieldVal = AUDITConfig.getConfigStringValue("config.TabSECTIONS_Department.T" + String.valueOf(fieldCount), "NotUsed");
                     if (!fieldVal.equalsIgnoreCase("NotUsed")){
                          TableParams_Department = fieldVal.split(",");
                          TabSECTIONS_Department[paramCount] = TableParams_Department[0];
                          nProps = Integer.parseInt(TableParams_Department[1]);
                          noDepartmentprops[paramCount] = nProps;
                          for (int i = 0; i < nProps; i++)  {
                              Department_props[paramCount][i] = TableParams_Department[i+2];
                         }
                         paramCount++;
                     }
                    fieldCount++;
               }
               Integer noDepartmentSections = paramCount;
               //ASB 15-08-2022 Create tab headers and properties for each section in the pdf
               fieldCount = 0;
               //Retrieve Tab Q Table parameters and Case Fields
                paramCount = 0;
                fieldCount = 0; //set for loading Fields
                fieldVal="";
               float[] columnWidths = {5, 1, 3.2f}; //Defaults for 3 columns
               //ASB  Retrieve the Column Name parameters from the config.xml
               for (String field:ColumnNameT4){
                    //ASB Returns NotUsed string for any elements not in the config.xml file
                    fieldVal = AUDITConfig.getConfigStringValue("config.PDFTable4.C" + String.valueOf(fieldCount), "NotUsed");
                     if (!fieldVal.equalsIgnoreCase("NotUsed")){
                          TableParams = fieldVal.split(",");
                          ColumnNameT4[paramCount] = TableParams[0];
                          ColumnWidthT4[paramCount] = TableParams[1];
                          columnWidths[paramCount] = Float.parseFloat(ColumnWidthT4[paramCount]);
                         paramCount++;
                     }
                    fieldCount++;
               }
               fieldCount = 0;
               PdfPTable table = new PdfPTable(columnWidths);
               try
               {   // Get data from the passed  Task ID Where CmAcmTaskName = AUD_ProduceFullQ
                    // From CmAcmCaseTask
                   SearchSQL sqlObject = new SearchSQL();
                   sqlObject.setSelectList("f.*");
                   Integer maxRecords = 10 ;//Should always be just one returned Object anyway!
                   sqlObject.setFromClauseInitialValue(TaskName, "f", true); //set true to include subclasses
                   sqlObject.setWhereClause("[Id] = "  + TaskGUID ); //ASB 11-08-2022
                    ObjectStore objectStore = getObjectStore("OS2");  //ASB TODO Change to load from properties
                   SearchScope search = new SearchScope(objectStore);
                   PropertyFilter myFilter = new PropertyFilter();
                   int myFilterLevel = 2; //Changed from 1 to 2 -- For performance !
                   myFilter.setMaxRecursion(myFilterLevel);
                   myFilter.addIncludeType(new FilterElement(null, null, null, FilteredPropertyType.ANY, null));
                   // Set the (Boolean) value for the continuable parameter. This indicates
                   // whether to iterate requests for subsequent pages of result data when the end of the
                   // first page of results is reached. If null or false, only a single page of results is
                   // returned.
                   Boolean continuable = new Boolean(true); //SET To allow more results to be fetched
                   // Set the page size (Long) to use for a page of query result data. This value is passed
                   // in the pageSize parameter. If null, this defaults to the value of
                   // ServerCacheConfiguration.QueryPageDefaultSize.
                   Integer myPageSize = new Integer(1000);
                   // Execute the fetchObjects method using the specified parameters.
                   //ASB added set myFilter to null REF Best Practice API on FNRCA0024E: API_PROPERTY_NOT_IN_CACHE
                   IndependentObjectSet myObjects = search.fetchObjects(sqlObject, myPageSize, null, continuable); //ASB myFilter  changed to null
//===============================================================S T A R T
                   // You can then iterate through the collection of rows to access the properties.
                  String [][] sTableCells = new String[nQuestions +1 ][nQuestionColumns +1]; //Could be max of nQuestion rows in the table
                String [] publishYes = new String[nQuestions +1];
                String [] questionValue = new String[nQuestions +1];
                String [] questionDetails =  new String[nQuestions +1];
                   int rowCount = 0;
                   Iterator iter = myObjects.iterator();
                 Paragraph sections = new Paragraph();
                    while (iter.hasNext()){   //Should be just one Task ID  !
                         try
                         {
                             //Folder f = null;
                            IndependentObject object = (IndependentObject) iter.next();
                            com.filenet.api.property.Properties props = object.getProperties();
                            Iterator iterProps = props.iterator();
                          for (int index = 0; index < nQuestions  ; index++)
                          {
                               //Initialise to empty
                               publishYes[index] = "No";
                               questionDetails[index] ="Not Used";
                               questionValue[index]  ="Not Used";
                               sTableCells[index][1] = questionValue[index];
                               sTableCells[index][2] = publishYes[index];
                               sTableCells[index][3] = questionDetails[index];
                          }
//============================ S T A R T ================================
                        // Call Routine to retrieve the Question arrays
                           CmTask currentTask = (CmTask) object;
                           String parentTaskClass = "CmTask"; //ASB 11-08-2022
                           String taskClass = currentTask.getClassName();
                           //ASB 06-08-2022 Get the Current Case Folder Task Property definitions
                            com.filenet.api.admin.ClassDefinition tClassDefs = Factory.ClassDefinition.fetchInstance(objectStore,taskClass, null); //update myFilter to null!!! ASB 06/08/2022
                            PropertyDefinitionList tPropList = tClassDefs.get_PropertyDefinitions();
                            //ASB Get the Current Task Object properties
                            //AUDITOR================================
                            AUDITOR_propValues = new String[noAUDITORSections ][100]; //Could be max of property values in the section
                            AUDITOR_propNames = new String[noAUDITORSections ][100]; //Could be max of property names in the section
                            getTaskAUDITORPropsSections(currentTask, taskClass,tClassDefs,tPropList,props,iterProps,AUDITOR_props,TabSECTIONS_AUDITOR,AUDITOR_propValues,AUDITOR_propNames,noAUDITORSections,noAUDITORprops);
                            //AUDITOR================================
                            Department_propValues = new String[noDepartmentSections ][20]; //Could be max of property values in the section
                            Department_propNames = new String[noDepartmentSections ][20]; //Could be max of property names in the section
                            getTaskDepartmentPropsSections(currentTask, taskClass,tClassDefs,tPropList,props,iterProps,Department_props,TabSECTIONS_Department,Department_propValues,Department_propNames,noDepartmentSections,noDepartmentprops);
                            Audit_propValues = new String[noAuditSections][20]; //Could be max of property values in the section
                            Audit_propNames = new String[noAuditSections ][20]; //Could be max of property names in the section
                            getTaskAuditPropsSections(currentTask, taskClass,tClassDefs,tPropList,props,iterProps,Audit_props,TabSECTIONS_Audit,Audit_propValues,Audit_propNames,noAuditSections,noAuditprops);  // 4. Populate the Case level Task Folder properties.
                           getTaskProps(currentTask, taskClass,tClassDefs,tPropList,props,iterProps,sTableCells, questionValue, publishYes, questionDetails,nQuestions +1,nQuestionColumns);
//=================================== E N D ==============================
                             //Set Table Column widths (Relative)
                             //float[] columnWidths = {1, 1, 1, 3, 4};
                             //float[] columnWidths = {1, 1, 1, 2, 5};
                           //AUDITOR START OUTPUT TO PDF
                           for(int iSection=0 ; iSection < noAUDITORSections ; iSection++) {
                               // We add one empty line
                               addEmptyLine(sections, 1);
                               // Lets write a big header
                               //TODO Pick all text up from the config.xml file
                               Paragraph paragraph = new Paragraph(TabSECTIONS_AUDITOR[iSection], catFont);
                               paragraph.setAlignment(Element.ALIGN_CENTER);
                               sections.add(paragraph);
                                  addEmptyLine(sections, 1);
                                for(int iProp=0 ; iProp < noAUDITORprops[iSection] ; iProp++) {
                                    paragraph = new Paragraph(AUDITOR_propNames[iSection][iProp] + " : " + AUDITOR_propValues[iSection][iProp], catFont);
                                    paragraph.setAlignment(Element.ALIGN_CENTER);
                                    sections.add(paragraph);
                                       addEmptyLine(sections, 1);
                                }
                           }
                           document.add(sections);
                           // Start a new page
                           document.newPage();
                             //END
                           //AUDITOR END OUTPUT TO PDF
                           //SECTION 0 TO 3 TABS
                           for(int iSection=0 ; iSection < 4 ; iSection++) {
                               // We add one empty line
                               addEmptyLine(sections, 1);
                               // Lets write a big header
                               //TODO Pick all text up from the config.xml file
                               Paragraph paragraph = new Paragraph(TabSECTIONS_Department[iSection], catFont);
                               paragraph.setAlignment(Element.ALIGN_CENTER);
                               sections.add(paragraph);
                                  addEmptyLine(sections, 1);
                                for(int iProp=0 ; iProp < noDepartmentprops[iSection] ; iProp++) {
                                    paragraph = new Paragraph(Department_propNames[iSection][iProp] + " : " + Department_propValues[iSection][iProp], catFont);
                                    paragraph.setAlignment(Element.ALIGN_CENTER);
                                    sections.add(paragraph);
                                       addEmptyLine(sections, 1);
                                }
                           }
                           document.add(sections);
                           sections = new Paragraph();
                           for(int iSection=4 ; iSection < noAuditSections ; iSection++) {
                               // We add one empty line
                               addEmptyLine(sections, 1);
                               // Lets write a big header
                               //TODO Pick all text up from the config.xml file
                               Paragraph paragraph = new Paragraph(TabSECTIONS_Department[iSection], catFont);
                               paragraph.setAlignment(Element.ALIGN_CENTER);
                               sections.add(paragraph);
                                  addEmptyLine(sections, 1);
                                for(int iProp=0 ; iProp < noDepartmentprops[iSection] ; iProp++) {
                                    paragraph = new Paragraph(Department_propNames[iSection][iProp] + " : " + Department_propValues[iSection][iProp], catFont);
                                    paragraph.setAlignment(Element.ALIGN_CENTER);
                                    sections.add(paragraph);
                                       addEmptyLine(sections, 1);
                                }
                           }
                           document.add(sections);
                           // Start a new page
                           document.newPage();
                           //SECTION 4 TAB
                             table.setWidthPercentage(95);
                             table.getDefaultCell().setUseAscender(true);
                             PdfPCell c1 = new PdfPCell(new Phrase(ColumnNameT4[0]));
                             c1.setHorizontalAlignment(Element.ALIGN_CENTER);
                             table.addCell(c1);
                             PdfPCell c2 = new PdfPCell(new Phrase(ColumnNameT4[1]));
                             c2.setHorizontalAlignment(Element.ALIGN_CENTER);
                             table.addCell(c2);
                             PdfPCell c3 = new PdfPCell(new Phrase(ColumnNameT4[2]));
                             c3.setHorizontalAlignment(Element.ALIGN_CENTER);
                             table.addCell(c3);
                             //PdfPCell c4 = new PdfPCell(new Phrase("Description"));
                             //c4.setHorizontalAlignment(Element.ALIGN_LEFT);
                             //table.addCell(c4);
                             //PdfPCell c5 = new PdfPCell(new Phrase("Description"));
                             //c5.setHorizontalAlignment(Element.ALIGN_LEFT);
                             //table.addCell(c5);
                             table.setHeaderRows(1);
                                //We have all the Questions loaded now
                            int iCell = 0;
                            int iRow = 0;
                             String titleVal = null;
                                   for  (String qRow:publishYes) {
                                        //Limit to the passed question number range //ASB 09-08-2022 14:00
                                     if (qRow != null ) {
                                             String [] cellRow = new String[nQuestionColumns];
                                             cellRow[0] =  questionValue[iRow+1];
                                             cellRow[1] =  publishYes[iRow+1];
                                             cellRow[2] =  questionDetails[iRow+1];
                                             if((iRow+1 >= nStartQNO ) && ( iRow+1 <= nEndQNO)) {
                                               for (String sCell:cellRow){
                                                    titleVal =sCell;
                                                   table.addCell(sCell);
                                                   iCell ++;
                                               }
                                            }
                                     }
                                             iCell = 0;
                                         iRow ++;
                               }
                                   //START
                                //Audit SECTION 0 TO 9 TABS
                                for(int iSection=0 ; iSection < noAuditSections ; iSection++) {
                                    // We add one empty line
                                    addEmptyLine(sections, 1);
                                    // Lets write a big header
                                    //TODO Pick all text up from the config.xml file
                                    Paragraph paragraph = new Paragraph(TabSECTIONS_Audit[iSection], catFont);
                                    paragraph.setAlignment(Element.ALIGN_CENTER);
                                    sections.add(paragraph);
                                       addEmptyLine(sections, 1);
                                     for(int iProp=0 ; iProp < noAuditprops[iSection] ; iProp++) {
                                         paragraph = new Paragraph(Audit_propNames[iSection][iProp] + " : " + Audit_propValues[iSection][iProp], catFont);
                                         paragraph.setAlignment(Element.ALIGN_CENTER);
                                         sections.add(paragraph);
                                            addEmptyLine(sections, 1);
                                     }
                                }
                                document.add(sections);
                                // Start a new page
                                document.newPage();
                                  //END
                    }
                             catch(Exception errprop){
                        }
                   //                   lastGoodPropName = propName;
             }
                    //Add the created table to the PDF Document
                    document.add(table);
               document.close();
               //Now delete the Text File created earlier
         if(fTextFile.exists()){
                    fTextFile.delete();
         }
          } catch (DocumentException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
               status = e.getMessage();
          }finally {
               status = "processed";
          }
          }catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    status = e.getMessage();
         } catch (DocumentException  e1) {
               // TODO Auto-generaed catch block
               e1.printStackTrace();
          }
          return pdfFile;
     }
    public String SendAlertEmailSSL(String to ,String from, String fromPassword,String host,String Subject, String Body,String SSLPort,String TLSPort )
    {
         // Uses the SMTP Gateway to send an email notification to the passed user(s)
           // Get system properties
            Properties properties = System.getProperties();
            final String sFrom = from;
            final String sFromPassword = fromPassword;
        // Setup mail server
           properties.setProperty("mail.smtp.host", host);
           properties.setProperty("mail.smtp.port", SSLPort); //SSLPort (gmail is 465)
         //May need TLS support
                properties.put("mail.smtp.starttls.enable","true");
         //Need SSL support - mail.smtp.ssl.enable
                properties.put("mail.smtp.ssl.enable","true");
                properties.put("mail.smtp.auth", "true");  //Authentication is required (At the moment!!)
         // Need the following for SSL
                 properties.put("mail.smtp.socketFactory.port", SSLPort); //SSL Port
                 properties.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
                 properties.put("mail.smtp.socketFactory.fallback", "false");
           // Get the default Session object.
           Session session = Session.getDefaultInstance(properties,new javax.mail.Authenticator() {
              protected PasswordAuthentication getPasswordAuthentication() {
                  return new PasswordAuthentication(sFrom, sFromPassword);
            }
          });
           try{
              // Create a default MimeMessage object.
              MimeMessage message = new MimeMessage(session);
              // Set From: header field of the header.
              message.setFrom(new InternetAddress(sFrom));
              // Set To: header field of the header.
              message.addRecipient(Message.RecipientType.TO,
                                       new InternetAddress(to));
              // Set Subject: header field
              message.setSubject(Subject);
              // Now set the actual message
              message.setText(Body);
              Transport t = session.getTransport("smtp");
              try {
                   t.connect(host, sFrom, sFromPassword);
                   t.sendMessage(message, message.getAllRecipients());
                 } finally {
                   t.close();
                 }
              return "Sent message successfully....";
           }catch (MessagingException mex) {
              return mex.getMessage();
           }
    }
    private void  getTaskProps(CmTask object,String taskClass,com.filenet.api.admin.ClassDefinition tClassDefs, PropertyDefinitionList tPropList,com.filenet.api.property.Properties props, Iterator iterProps, String [][] sTableCells, String [] questionValue,String [] publishYes,String [] questionDetails, int nQuestions,int nQuestionColumns)throws Exception {
//      private void getTaskProps(Folder currentFolder,Folder f,com.filenet.api.admin.ClassDefinition fClassDefs,Properties props           ,Iterator iterProps) throws Exception {
    // called as :
     //                getTaskProps(currentTask, taskClass,tClassDefs,tPropList,props,iterProps,questionValue,publishYes,questionDetails);
      //    String [][] sTableCells = new String[nQuestions +1 ][nQuestionColumns+1]; //Could be max of nQuestion rows in the table
    //    String [] publishYes = new String[nQuestions+1];
    //    String [] questionValue = new String[nQuestions+1];
    //    String [] questionDetails =  new String[nQuestions+1];
           //ASB
           //Retrieve the current property list to retrieve based on the Task Class definitions from the Object Store
               String tClassName = object.getClassName(); //ASB 03-08-2022
               com.filenet.api.admin.ClassDefinition classDefs = tClassDefs;
               PropertyDefinitionList propList = tPropList;
            StringBuffer tempBuf = new StringBuffer();
               //Retrieve property names code From writeDocProps to compare
               Iterator iter =propList.iterator();
               while (iter.hasNext())
               {
                  try {
                       PropertyDefinition propDef = (PropertyDefinition) iter.next();
                       String propName = propDef.get_SymbolicName();
                       //ASB ... 2 011 Need to skip Date Last Accessed etc - ReadOnly error here
             //          ||propName.equalsIgnoreCase("Permissions")  //ASB removed 25/08/2022
                       if (!(propName.equalsIgnoreCase("Name")
                                  ||propName.equalsIgnoreCase("DateCheckedIn")
                                 ||propName.equalsIgnoreCase("CurrentState")
                                 ||propName.equalsIgnoreCase("PathName")
                                 ||propName.equalsIgnoreCase("LockOwner")
                                 ||propName.equalsIgnoreCase("Owner") //ASB 28-08-2022
                                 ||propName.startsWith("CmAcm") )){
                            //ASB ... Set to check property cardinality
                            int CardinalityVal = propDef.get_Cardinality().getValue();
                            switch (propDef.get_DataType().getValue()){
                            case StringType:
                                 if (CardinalityVal == CardinalitySINGLE){
                                      String val = object.getProperties().getStringValue(propName);
                                    for (int index = 1; index < nQuestions; index++)
                                    {
                                     // Value for Required Question
                                          String QValue = val ;
                                         if (propName.equalsIgnoreCase("AUD_Report4_QYN_" + index))
                                         {
                                               //Checks if this is set to a required response line
                                                    publishYes[index] = QValue;
                                                    sTableCells[index][2] = publishYes[index];
                                           }
                                        // Question Value for Required Question Details
                                          if (propName.equalsIgnoreCase("AUD_Report4_QDetails_" + index))
                                          {
                                                     questionDetails[index] = QValue;
                                                     sTableCells[index][3] = questionDetails[index];
                                          }
                                        // Question Value for Required Question
                                          if (propName.equalsIgnoreCase("AUD_Report4_AUD_" + index))
                                          {
                                                     questionValue[index] = QValue;
                                                      sTableCells[index][1] = questionValue[index];
                                          }
                                      }
                                      //ASB 11-08-2022
                                      //f.getProperties().putValue(propName, val);
                                      if (propName.equalsIgnoreCase("lastmodifier")){
                                           //f.set_LastModifier(val);
                                      }
                                      if (!propName.equalsIgnoreCase("creator") && !propName.equalsIgnoreCase("lastmodifier")){
                                           //f.getProperties().putValue(propName, val);
                                      }
                            }else{
                                 //StringList valList = currentFolder.getProperties().getStringListValue(propName);
                                 //f.getProperties().putValue(propName, valList);
                            }
                                 // Process Multi-value Strings (if required)?
                                 break;
                            case DateType:
                                 if (CardinalityVal == CardinalitySINGLE){
                                      //Date dateVal = currentFolder.getProperties().getDateTimeValue(propName);
                                      //if (propName.equalsIgnoreCase("datelastmodified")){
                                      //     currentFolder.getProperties().removeFromCache("DateLastModified");
                                      //     f.set_DateLastModified(dateVal);
                                 }
                                      //ASB ... 4 029 Start
                                      if (propName.equalsIgnoreCase("datecreated")){
                                           //currentFolder.getProperties().removeFromCache("DateCreated");
                                           //f.set_DateCreated(dateVal); //ASB 19-08-2022 Bug fix on Folder Date from dateLast Modified!
                                         //if (lastFolderDateCreated.compareTo(dateVal) < 0) {
                                         //     lastFolderDateCreated = dateVal;
                                         //}
                                      }
                                      //ASB ... 4 029 End
                                if (!propName.equalsIgnoreCase("datecreated") && !propName.equalsIgnoreCase("datelastmodified")){
                                     //f.getProperties().putValue(propName, dateVal);
                                }
                                  break;
                                 default:
                               }
                            }
                              }catch (Exception writeProp){
                                   logger.error(writeProp.getMessage(), writeProp);
                              }
                         } //for (int iProp = 0;iProp < xmlprops.getLength();iProp ++){
                    }
    //========S T A R T    C O D E    F O R    P D F   I M P O R T ========
     //Main Process loop for the Import Documents process.
     //
     private void importDocuments(String CaseGUID,String TaskGUID,String TaskName,String pdfFileName,String WorkingDirectory, String[] DocTitles,com.filenet.api.core.Document [] docList, int nDocs) throws Exception {
         BufferedWriter auditFileWriter;
         Long importStartTime;
         Long docImportStartTime;
         Long timeToImport;
            long maxRunTimeMillis;
//         boolean moreDocsToImport=true;
         Integer consecutiveErrs=0;
         Integer errorCount=0;
         boolean completed=false;
         this.processedCount = 0;
         String LineSave="";
         importStartTime = System.currentTimeMillis();
         maxRunTimeMillis = cemc.getImportMaxRunTimeMinutes()*60*1000;
         Long maxDocCount = new Long(cemc.getImportMaxDocCount());
         auditFileWriter = new BufferedWriter(new FileWriter(cemc.getImportAuditFile()));
         auditFileWriter.write("Import Start - ");
         auditFileWriter.newLine();
         auditFileWriter.write("GUID, SOURCEVSID, DESTDOCID, STATE, Millisecs");
         auditFileWriter.newLine();
         //ub = UpdatingBatch.createUpdatingBatchInstance(os.get_Domain(), RefreshMode.NO_REFRESH);
          File pdfFile =  new File(pdfFileName);
          //get all the files from a directory, based on the config File Filter (default *.pdf)
         //File directory = new File(WorkingDirectory); //Set in config.xml
         //    ArrayList<File> files  = new ArrayList<File>();
         //    this.listf(WorkingDirectory, files,pdfFileName);
         //    File[] fList = files.toArray(new File[]{});
          String  documentFile ="";
          String documentFileName ="";
          String documentFilePath = "";
          //for (int i = 0; i < fList.length; i++) {
               documentFile = pdfFile.toString();
               documentFileName = pdfFile.getName();
               for(int idoc=0;idoc < nDocs;idoc++){
                 if(documentFileName.equalsIgnoreCase(DocTitles[idoc])) {
                      return; //ASB 14/10/1066
                      // Change to just ever allow one pdf production as the task is only converted after it is complete
                      // ie is already immutable!!!
                     //String mimeType = cemc.getMimeType();
                     //updateNewDocument(docList[idoc],documentFileName, mimeType, documentFile);
                       //========END DOCUMENT VERSION PROCESSING==========
                      //return;
                 }
               }
               documentFilePath = pdfFile.getPath();
              //ASB ... 022 - Retrieve Date start from config.xml
              String sDeltaHours = cemc.getDeltaHours();
              String sSearchDate = cemc.getStartSearchDate();
              logger.info("Start Search Date : " + sSearchDate);
              logger.info("Start Delta Hours : " + sDeltaHours);
               //ASB ... 4 002 Now dynamically update the next Loader Date
               cemc.updateProcessDate(new Date());
              logger.info("File Imported: " + documentFile);
//              Integer myPageSize = new Integer(1000);
              // Specify a property filter to use for the filter parameter, if needed.
              // This can be null if you are not filtering properties.
              PropertyFilter myFilter = new PropertyFilter();
              int myFilterLevel = 1;
              myFilter.setMaxRecursion(myFilterLevel);
              //ASB ... 4 020
              if(cemc.getDebugOutputFlag().equalsIgnoreCase("off")){
                   myFilter.addIncludeType(new FilterElement(null, null, null, FilteredPropertyType.ANY_LIST, null));
                   myFilter.addIncludeType(new FilterElement(null, null, null, FilteredPropertyType.ANY_SINGLETON, null));
              }else{
                   myFilter.addIncludeType(new FilterElement(null, null, null, FilteredPropertyType.ANY, null));
              }
                        //U P D A T E - Iterate through the Directory of input Documents with pdf properties
                        Id  GUID = null;
                        docImportStartTime = System.currentTimeMillis();
                        Boolean importFailure = false;
                        Boolean filedDoc = false;
                        Document d = null;
                        String documentTitle = "";
                        //ASB ... 3 005 - Retrieve Folder list for checks
                        //              - (Only Load documents in config Path)
                        String [] sDocFolderPath = new String [1000]; //TODO set max document Folders Filed In from config.xml
                        int folderCount = -1;
                        String lastGoodPropName = "";
                        String propName = "";
                        String propValue = " ";
                        //Extract the  comma separated lines
                        //String docProps[] = line.split(",");
                        //String docPropNames[] = new String[docProps.length];
                        //Integer docPropTypes[] = new Integer[docProps.length];
                        //Integer docPropCardinality[] = new Integer[docProps.length];
                        //String docPropFlags[] = new String[docProps.length]; //ASB IBM - Added 2nd October
                        //for (int j = 0; j < docProps.length; j++) {
                             //Strip out the double quotes (if present)
                        //     if(docProps[j] != null && docProps[j].length() > 1 && docProps[j].substring(1,2).equalsIgnoreCase(""")){
                        //          docProps[j] = docProps[j].substring(1,docProps[j].length() -1);
                        //     }
                        //     propName = SolutionPrefix + cemc.getPropName(j);
                        //     if(docProps[j]==null || docProps[j].length() == 0){
                        //          docProps[j]=" ";
                        //     }
                        //     propValue = docProps[j].replace("'","");//Remove any single quote values!!
                        //     docPropNames[j]= propName;
                        //     docPropTypes[j]=cemc.getPropType(j);
                        //     docPropCardinality[j]=cemc.getPropCardinality(j);
                        //     docPropFlags[j]=cemc.getPropFlags(j); //ASB IBM - Added 2nd October
                        //ASB IBM - Added 2nd August - Check if this is a placeholder or a Boolean list property
                        //     if (!docPropFlags[j].equalsIgnoreCase("NONE")){
                        //          docPropFlags[j] = SolutionPrefix + docPropFlags[j];
                        //     }
                        //     try{
                        //          if(cemc.getDebugOutputFlag().equalsIgnoreCase("off")){
                        //          }else{
                        //               logger.info("Property: " + propName );
                        //               logger.info("Value   : " + propValue );
                        //          }
                        //          if (propName.equalsIgnoreCase(SolutionPrefix + "FileName"))
                        //          {
                        //               documentTitle = propValue;
                        //               documentFile = propValue;
                        //          }
                        //            sDocFolderPath[1] = cemc.get_DocPathName();
                        //     }catch(Exception errprop){
                        //          logger.info("Document Title :" + documentTitle + " : Property  " + propName + " Caused Error : " + errprop.getMessage() + " : Last Good property :" + lastGoodPropName);
                        //     }
                        //     lastGoodPropName = propName;
                        //}
                  //E N D -- DEBUG OFF/ON SECTION
                        documentTitle = documentFileName;
                        if (documentTitle.equalsIgnoreCase("")) return; //Changed from break
                        try {
                             if (!importFailure )
                             {
                                  docImportStartTime = System.currentTimeMillis();
                                  d = createDocument(documentTitle, auditFileWriter,GUID,documentFile);
                                  Long runTimeMillis = System.currentTimeMillis() - importStartTime;
                                  //Folder the document in the Case Folder
                                 //Get the Case Folder GUID ,String TaskGUID,String TaskName,
                                  String sFolderId = CaseGUID;
                                Folder caseFolder =fetchFolderById( os, sFolderId);
                                //Folder caseFolder = getFolderFromID(folderGUID,objectStoreName,CMFolderClass);
                                  if (caseFolder != null){
                                       com.filenet.api.core.ReferentialContainmentRelationship rcr = caseFolder.file(d,AutoUniqueName.NOT_AUTO_UNIQUE,null,DefineSecurityParentage.DO_NOT_DEFINE_SECURITY_PARENTAGE);
                                    rcr.save(RefreshMode.NO_REFRESH);
                                  }
                                  processedCount ++;
                                  //importProcessedDocCount ++;
                                  if (this.processedCount == maxDocCount  && maxDocCount>0)
                                       {
                                       completed = true;
                                       logger.info("Processing Completed Max Document Count Reached. ");
                                       }
                                  else if ((runTimeMillis > maxRunTimeMillis)&& maxRunTimeMillis>0 )
                                       {
                                       completed = true;
                                       logger.info("Processing Completed Max Processing Time Reached.");
                                       }
                                  consecutiveErrs=0;  // success so initialise the consecutive errs count
                             }
                        }
                        catch (Exception ceme){
                        }
                   //  }
     //}
                   //ASB 01/12/2020 Need to close Buffered Reader to allow file rename to work
                   //br.close(); //ASB Close File I/O
             // {
              //Rename file
              //fList[i].renameTo("doc_fact.cvs_" + System.currentTimeMillis());
                //File newName = new File(fList[i].getAbsolutePath() + "_" + System.currentTimeMillis() );
                //if(fList[i].renameTo(newName)) {
                //   System.out.println("Renamed " + "doc_fact.csv" + " to " + newName.getName() );
                //} else {
                //   System.out.println("Error");
               // }
          //}
          auditFileWriter.write("Finished - time taken " + (System.currentTimeMillis() - importStartTime) + "milliseconds");
          auditFileWriter.newLine();
          auditFileWriter.write("Finished - documents processed - " + String.valueOf(processedCount));
          auditFileWriter.newLine();
          auditFileWriter.write("Finished - documents failed to be processed - " + errorCount.toString());
          auditFileWriter.newLine();
        //ASB ... 4 031 Add retry count for Content
          //auditFileWriter.write("Finished - document total 'Add Content Retry Count' - " + String.valueOf(retryTotalProcessedCount));
          auditFileWriter.newLine();
          auditFileWriter.close();
          //}
     }
    //ASB 19th November 2020
    public void listf(String directoryName, ArrayList<File> files,String filenamePattern) {
        File directory = new File(directoryName);
         // create new filename
        // get all the files from a directory
        File[] fList = directory.listFiles();
        for (File file : fList) {
            if (file.isFile()) {
                 if(file.toString().endsWith(".pdf")){
                      files.add(file);
                 }
            } else if (file.isDirectory()) {
                listf(file.getAbsolutePath(), files,filenamePattern);
            }
        }
    }
     //Creates a com.filenet.api.core.Document object
     private Document createDocument(String documentTitle, BufferedWriter auditFileWriter,Id GUID, String documentFile) throws  Exception, EngineRuntimeException {
         Document d = null;
          try
          {
               Boolean isReserved = false;
               String documentclass = cemc.getDocClassName(); //Read the required Class from cemc
             Long docImportStartTime;
             Long timeToImport;
             docImportStartTime = System.currentTimeMillis();
            //ASB ... 2 014 Get the Version Numbers of the Document
            Integer docMajorVersion = 1;
            Integer docMinorVersion = 0;
              PropertyFilter myFilter = new PropertyFilter();
              int myFilterLevel = 2; //Changed from 4 to 2 -- Set as a parameter
              myFilter.setMaxRecursion(myFilterLevel);
              myFilter.addIncludeType(new FilterElement(null, null, null, FilteredPropertyType.ANY, null));
                   try {
                        docImportStartTime = System.currentTimeMillis();
                        d = Factory.Document.createInstance(os, documentclass, null);
                        //Write to audit log
                         timeToImport = System.currentTimeMillis() - docImportStartTime;
                         //auditFileWriter.write(d.get_Id().toString() + "," + d.get_VersionSeries().get_Id().toString() + " ,Created," + timeToImport.toString());
                         //auditFileWriter.newLine();
                   }catch(Exception e)
                      {
                    //If the Exception is this exists then delete and retry add again
                        logger.error("Error creating the Document: " + documentTitle + "");
                   }
                 d.getProperties().putValue("DocumentTitle", documentTitle);
                    //writeDocProps(d, docPropNames, docPropFlags, docProps, docPropTypes, docPropCardinality,documentTitle );
                         this.processedCount = this.processedCount +1;
                         //Write to audit log
                        timeToImport = System.currentTimeMillis() - docImportStartTime;
                        //auditFileWriter.write("GUID :"  + d.get_Id().toString() + ",Properties Updated," + timeToImport.toString());
                        //auditFileWriter.newLine();
                    //ADD CONTENT
          int retryCount = 0;
          FileInputStream fis = null;
          int retryLimit = 5; //TODO Maybe set this as a configuration parameter
          while (retryCount < retryLimit){
               try {
                     com.filenet.api.core.ContentTransfer ct = Factory.ContentTransfer.createInstance();
                    File file = new File(documentFile );
                    fis = new FileInputStream(file);
                    InputStream str = fis;
                    ct.setCaptureSource(str);
                    // Add Document Title
                    ct.set_RetrievalName(documentTitle);
                    // Add Content Mime Type
                    ct.set_ContentType(cemc.getMimeType());
                    ContentElementList cel = Factory.ContentElement.createList();
                    cel.add(ct);
                    d.set_ContentElements(cel);
                    break;
                    }
                    catch (Exception e){
                         String testMessage = e.getMessage();
                         String testCode = e.toString();
                         if( testMessage.contains("A uniqueness requirement has been violated")){
                              retryCount = retryLimit;
                              break;
                         }
                         //  ASB ... 4 025 Log Exception here
                         logger.error(e.getMessage(), e);
                         retryCount ++;
                         //retryTotalProcessedCount ++;
                         logger.info("Retry Count - createDocument method : " + retryCount + " of " + retryLimit + " On : " + documentTitle + " : with GUID : " + GUID);
                    }finally {
                    }
          } //ASB ... 4 031 Above loops Test retryCount less than the retryLimit of 7
                //ASB ... 4 028 Change to do not Classify from AutoClassify.AUTO_CLASSIFY, to
                   d.checkin(AutoClassify.DO_NOT_AUTO_CLASSIFY, CheckinType.MAJOR_VERSION);
                   d.save(RefreshMode.REFRESH);
                    return d;
               }catch(EngineRuntimeException ere)
                 {
                    //ASB ... 2 005
                   // Create failed.  See if it's because the Document exists.
                   ExceptionCode code = ere.getExceptionCode();
                   if (code.getErrorId() != ExceptionCode.DB_NOT_UNIQUE.getErrorId() )
                   {
                        logger.error("Unexpected Error : " + documentTitle + " Error stack: " + ere.getStackTrace());
                       throw ere;
                   }
                   logger.error("Document already exists: " + documentTitle + "");
                       //ASB ... 2 006 Delete document in the Target Object Store and Recreate
                      //this.deleteDoc(GUID.toString());
                       //ASB ... 2 007 Fetch the Source Object Document to be re-created
                        PropertyFilter myFilter = new PropertyFilter();
                        int myFilterLevel = 2; //Changed from 4 to 2 -- Set as a parameter
                        myFilter.setMaxRecursion(myFilterLevel);
                        myFilter.addIncludeType(new FilterElement(null, null, null, FilteredPropertyType.ANY, null));
                         String documentclass = cemc.getDocClassName();
                       Document dInput = Factory.Document.fetchInstance(os, GUID, myFilter);
                    //Create Version Series for the document in the Target Object Store
                       com.filenet.api.core.VersionSeries versions = dInput.get_VersionSeries();
                        //Get the Document Versions to be added from the Source Document
                        VersionableSet verSet = versions.get_Versions();
                        Iterator versIt = verSet.iterator();
                        int versCount = 0;
                        int majorVn = 0;
                        int minorVn = 0;
                        Document doc = null; //Input Document versions
                        Id createdGuid = null;
                        while (versIt.hasNext()){
                              versCount ++;
                             doc = (Document)versIt.next();
                             majorVn = doc.get_MajorVersionNumber();
                             minorVn = doc.get_MinorVersionNumber();
                             //if (!isReserved)isReserved = doc.get_IsReserved();
                             Id nextGUID = doc.get_Id();
                            createdGuid = d.get_Id();
                                   if (versCount == 1){
                                    FolderSet fs = doc.get_FoldersFiledIn();
                                    Iterator iterFs = fs.iterator();
                                    while (iterFs.hasNext())
                                    {
                                        Folder folder = (Folder)iterFs.next();
                                        //Create/Check and Make link in Target for the Document to this Folder
                                           if(cemc.getDebugOutputFlag().equalsIgnoreCase("off")){
                                           }else{
                                                logger.info(" Folder Name: " + folder.get_FolderName() +
                                            "   Folder Path: " + folder.get_PathName());
                                           }
                                            folderDoc(d,folder.get_PathName(),documentTitle);
                                    }
                                   }
                              }
                             //ASB ... 2 020 Update Document ACL from source Document security
                             AccessPermissionList  aclIn = dInput.get_Permissions();
                             // Add the permission to the list for the Object Store.
                             //Get This group from the config.xml file
//                             String sExcludeGroup = cemc.getExcludedGroup();
//                             String sExcludeUser =  cemc.getExcludedUser();
                             //ASB ... 4 027 For Performance check if a full LDAP search is required
//                             if(cemc.getLDAPSearchFlag().equalsIgnoreCase("on")){
//                                  aclIn = addPermissions(aclIn, sExcludeGroup, sExcludeUser);
//                             }else {
//                                  aclIn = addPermissionsNoSearch(aclIn, sExcludeGroup, sExcludeUser);
//                             }
                             //ASB ... 4 027 aclIn = addPermissions(aclIn, sExcludeGroup, sExcludeUser);
                             d.set_Permissions(aclIn);
                 }
                    return d;
     }
     private void folderDoc(Document doc, String folderPathName, String documentTitle){
          String sContainmentName = documentTitle;      //FOR RCR: //ASB 17/02/2011 Add Containment name
          if (folderPathName != null && folderPathName.length() > 0){
               Folder folder = null;
               try {
                    folder = Factory.Folder.getInstance(os, null, folderPathName);
                    //folder = Factory.Folder.fetchInstance(os, folderName, null);
                    com.filenet.api.core.DynamicReferentialContainmentRelationship rcr =
                             Factory.DynamicReferentialContainmentRelationship.createInstance(os, null,
                                                AutoUniqueName.AUTO_UNIQUE,
                                                DefineSecurityParentage.DO_NOT_DEFINE_SECURITY_PARENTAGE);
                         rcr.set_Tail(folder);
                         rcr.set_Head(doc);
                         rcr.set_ContainmentName(sContainmentName);
                      rcr.save(RefreshMode.NO_REFRESH);
               }
               catch (Exception e){
               }
          }
     }
     private static Folder fetchFolderById(ObjectStore os, String id)
    {
        Id id1 = new Id(id);
        Folder folderCase = Factory.Folder.fetchInstance(os, id1, null);
        return folderCase;
    }
     //This method returns the count of the number of Document versions
     //(Unfortunately java Iterator class does not return size!)
     private int countVersions(com.filenet.api.core.VersionSeries versions){
          int versionCount=0;
          VersionableSet verSet = versions.get_Versions();
          Iterator versIt = verSet.iterator();
          int versCount = 0;
           maxMin = 0;
          int min = 0;
          Document doc = null; //Input Document versions
          while (versIt.hasNext()){
               versCount ++;
               doc = (Document)versIt.next();
             //Record largest minor version value for loop
             min = doc.get_MinorVersionNumber();
             if (min > maxMin){
                  maxMin = min;
             }
          }
          versionCount = versCount;
          return versionCount;
     }
     //This method returns the  Document versions in ascending order
     //(Unfortunately FileNet API iterator returns latest version first! )
     private Document[][] getDocumentVersionsArray(com.filenet.api.core.VersionSeries versions, Document [][] docVersions,int maxMin){
          int versionCount=0;
          VersionableSet verSet = versions.get_Versions();
          Iterator versIt = verSet.iterator();
          int versCount = 0;
          Document doc = null; //Input Document versions
          maxMin = 0;
          int min=0;
          int maj=0;
          while (versIt.hasNext()){
               versCount ++;
               doc = (Document)versIt.next();
               maj = doc.get_MajorVersionNumber();
               min = doc.get_MinorVersionNumber();
               //Record largest minor version value for loop
               if (min > maxMin){
                    maxMin = min;
               }
            docVersions[maj][min] = doc;
          }
          versionCount = versCount;
          return docVersions;
     }
     private Document createVersions(String documentTitle, Id GUID, Document object,Boolean isReserved,Document[][] docVersions,Integer majCount,int minCount, BufferedWriter auditFileWriter,com.filenet.api.core.VersionSeries versions)throws Exception{
          PropertyFilter myFilter = new PropertyFilter();
          int myFilterLevel = 2; //Changed from 4 to 2 -- TODO Set as a parameter
          myFilter.setMaxRecursion(myFilterLevel);
          myFilter.addIncludeType(new FilterElement(null, null, null, FilteredPropertyType.ANY, null));
          Document currentDocument = (Document) object;
          String documentclass = currentDocument.getClassName();
          Document dInput = Factory.Document.fetchInstance(os, GUID, myFilter);
          //Create Version Series for the document in the Target Object Store
          //VersionSeries versions = dInput.get_VersionSeries();
          //Get the Document Versions to be added from the Source Document
          VersionableSet verSet = versions.get_Versions();
          Iterator versIt = verSet.iterator();
          int versCount = 0;
          int majorVn = 0;
          int minorVn = 0;
          Document doc = null; //Input Document versions
          Document d = null;   //Output document versions
          isReserved = false;
          Id createdGuid = null;
          Id nextGUID = null;
          int foldCount = 0;
          //while (versIt.hasNext()){ ASB ... 2
          int  minVersCount = -1;
          while (versCount < majCount){
                 versCount ++;
               while (minVersCount < minCount){
                        minVersCount ++;
                  doc = docVersions[versCount][minVersCount];
                  if(doc == null){
                     break;
                  }
                 foldCount ++;
                  majorVn = doc.get_MajorVersionNumber();
                  minorVn = doc.get_MinorVersionNumber();
                  nextGUID = doc.get_Id();
                  //ASB ... 4 004 Get the Release Version
                  VersionStatus dVersionStatus = doc.get_VersionStatus();
                  if (!isReserved)isReserved = doc.get_IsReserved();
               //Need to update for each version in turn
                  d = createVersion(versCount, d, doc, isReserved, majorVn,minorVn, documentTitle, nextGUID, createdGuid, nextGUID,auditFileWriter);
                  // try{
                        //createdGuid = nextGUID;
                  createdGuid = d.get_Id(); //ASB ... 4 025 Try/Catch Here especially as reservation has no folders!!
                // }catch(Exception createdEx){
                   //   logger.info("Version number "+ majorVn + "." + minorVn + " of " + documentTitle + " caused error : " + createdEx.getMessage());
                 // }
                  try {
                       if (foldCount == majCount){
                         FolderSet fs = doc.get_FoldersFiledIn();
                         Iterator iterFs = fs.iterator();
                         while (iterFs.hasNext())
                         {
                             Folder folder = (Folder)iterFs.next();
                             //Create/Check and Make link in Target for the Document to this Folder
                                 if(cemc.getDebugOutputFlag().equalsIgnoreCase("off")){
                                 }else{
                                      logger.info(" Folder Name: " + folder.get_FolderName() +
                                 "   Folder Path: " + folder.get_PathName());
                                 }
                                 folderDoc(d,folder.get_PathName(),documentTitle);
                         }
                         }
                 }catch(Exception FileErr){
                          logger.error(FileErr.getMessage(), FileErr);
                 }
                         if (isReserved){
                         break;
                    }
              }
               //ASB ... 4 019 Check at this point if we have reached the completed creation
               //              If
                    minVersCount = -1;
          }  // Next Major Version
               if (isReserved)
                    d = doReservationProperties(versCount, doc, d, isReserved, majorVn,minorVn,documentTitle);
          return d;
          }
     //Called by CreateDocument,  creates a version from the information in the XML node specified.
     //Creates a new reservation object, specifying properties from XML.
     //private Document createVersion(Document d) throws CEMigrateException, Exception {
          private Document createVersion(int      versCount, Document d, Document dInput, Boolean isReserved, int majorVn, int minorVn,String documentTitle, Id GUID, Id createdGUID, Id nextGUID,BufferedWriter auditFileWriter)     throws Exception {
               String guid = "";
               Long timeToImport = 0l;
               Id vGUID =null;
               Document res = null;
             Long docImportStartTime = System.currentTimeMillis();
               PropertyFilter myFilter = new PropertyFilter();
              int myFilterLevel = 2; //Changed from 4 to 2 -- For performance!
              myFilter.setMaxRecursion(myFilterLevel);
              myFilter.addIncludeType(new FilterElement(null, null, null, FilteredPropertyType.ANY, null));
              Document currDoc = null;
               try
                  {
               int thisMajorVersionNumber = majorVn;
               int thisMinorVersionNumber = minorVn; //ASB ... 3 004
               String docClass = dInput.getClassName();
               //ASB ... 3 006 Now get the current source Doc Version Series ID
               Id vId = dInput.get_VersionSeries().get_Id();
               if (!isClassExist(docClass))
                    throw new Exception("Document class " + docClass + " Does not exist");
               Id reservationId_null = null; String reservationClass_null = null; // to remind of the parameter types!
               boolean isMajorVersion = false;
               boolean justCreated = true;
               if (d == null){
                    if (thisMajorVersionNumber > 0)
                         isMajorVersion = true;
                    if (thisMinorVersionNumber > 0)
                        isMajorVersion = false;
                        //ASB ... 2 015 Added GUID to ensure we get the same later
                        //ASB ... 3 006 New Version of the first document create to store Version series ID
                        //ASB ... 3 006 Many thanks to David Greenhouse for supplying the call :-)
                        res = Factory.Document.createInstance(os, docClass, GUID, vId, com.filenet.api.constants.ReservationType.EXCLUSIVE);
                         //res = (Document)os.createObject(docClass,GUID); //ASB ... 3 006 Removed Old Creation
                         //Added this to ensure that these values were correctly set on the initial version
                         String mimetype = dInput.get_MimeType();
                         String filename = dInput.get_Name();
                     Date dateCreated = dInput.get_DateCreated();
                         res.set_MimeType(mimetype);
                         res.set_DateCreated(dateCreated); //ASB ... 4 029
                         //ASB 04/04/2011 - Update Creator and last Modifier and dates
                         String sCreator = dInput.get_Creator();
                         res.set_Creator(sCreator);     //ASB 04/04/2011
                         String sLastModifier = dInput.get_LastModifier();
                         res.set_LastModifier(sLastModifier);          //ASB 04/04/2011
                         Date dateLastModified = dInput.get_DateLastModified();
                         res.set_DateLastModified(dateLastModified); //ASB 04/04/2011
                     currDoc = dInput;
                     com.filenet.api.property.Properties docprops = Factory.Document.createInstance(os, null).getProperties(); //????
                     com.filenet.api.property.Properties props = res.getProperties();
                      Iterator iterProps = props.iterator();
                        writeSpecialDocProps(res, iterProps, justCreated, documentTitle, currDoc);
                     //ASB ... 4 023 Add Count for first version
                        this.processedCount = this.processedCount +1;
                       guid = GUID.toString().trim();
                         //Write to audit log
                        timeToImport = System.currentTimeMillis() - docImportStartTime;
                        auditFileWriter.write(guid + "," + vId.toString() + "," + guid + ",Imported," + timeToImport.toString());
                        auditFileWriter.newLine();
               }
               else {
                    justCreated = false;
                 //GUID here should be from next iteration set above
                    //ASB ... 2 016 Changed to osinput from os! try nextGUID
                    //currDoc = (Document)os.fetchObject("Document", createdGUID, myFilter);
                 //ASB ... 4 024 Update to support other Document Types
                    currDoc = (Document)os.fetchObject(docClass, createdGUID, myFilter);
                    com.filenet.api.property.Properties props = Factory.Document.createInstance(os, null).getProperties();
                    String sCreator = currDoc.get_Creator();
                    Date dateCreated = currDoc.get_DateCreated();
                    String sLastModifier = currDoc.get_LastModifier();
                    Date dateLastModified = currDoc.get_DateLastModified();
                    // This collection is used when we do the checkout
                    props.putValue("Creator", sCreator);
                    props.putValue("DateCreated", dateCreated);
                    props.putValue("LastModifier", sLastModifier);
                    props.putValue("DateLastModified", dateLastModified);
                    //ASB ... 4 024
                    //d = (Document)os.fetchObject(docClass, createdGUID, myFilter);
                    d = currDoc; //No need to fetch again!
                    //d = (Document)os.fetchObject("Document", createdGUID, myFilter);
                    //ASB ... 4 014 New Version needs to use the GUID passed in !!
                    String sNewGuid = GUID.toString();
                    // ASB Compare : d.checkout(ReservationType.EXCLUSIVE, GUID, docClass, null);
                    d.checkout(ReservationType.EXCLUSIVE, GUID, docClass, props); //ASB ... 4 029 Changed from null to props
                    //d.set_DateCreated(dateCreated); //ASB004 029 READONLY AT THIS POINT!!
                    d.save(RefreshMode.REFRESH);
                    this.processedCount = this.processedCount +1;
                  guid = GUID.toString().trim();
                    //Write to audit log
                   timeToImport = System.currentTimeMillis() - docImportStartTime;
                   auditFileWriter.write(guid + "," + d.get_VersionSeries().get_Id().toString() + "," + d.get_Id().toString() + ",Imported," + timeToImport.toString());
                   auditFileWriter.newLine();
                    // Get the reservation object
                    res = (Document)d.get_Reservation();
                    res.set_LastModifier(sLastModifier);        //ASB00 4
                    res.set_DateLastModified(dateLastModified); //ASB00 4 Uodate from dateCreated ??!!
                    //res.set_DateCreated(dateCreated); //ASB004 029 READ ONLY AT THIS POINT!!!
                    String mimetype = currDoc.get_MimeType();
                    props.putValue("MimeType", mimetype);
                    int mvn = majorVn;
                    int minvn = minorVn;
                    if (thisMajorVersionNumber > 0) isMajorVersion = true;
                    if (thisMinorVersionNumber > 0) isMajorVersion = false;
                 dInput = currDoc;
                 GUID = nextGUID; //Get Current Content from version
               }
                        try {
                         addContent(dInput, res, documentTitle,  myFilter, GUID,docClass);
                         }
                         catch (Exception e){
                              //ASB ... 4 Check if the Content could not be added because the document already exists, if so
                              //          Remove document and retry (next run)
                              //  ASB ... 4 025 Log Exception here
                              logger.error(e.getMessage(), e);
                         }
            if(isReserved){ //ASB ... 2 012 Required For Checkin the Document must be Status of reserved!!
                    if (isMajorVersion){
                         //ASB ... 4 028 Changed from NULL to AutoClassify.DO_NOT_AUTO_CLASSIFY
                         res.checkin(AutoClassify.DO_NOT_AUTO_CLASSIFY,CheckinType.MAJOR_VERSION);
                         res.save(RefreshMode.NO_REFRESH);
                    }
                    else {
                         res.checkin(AutoClassify.DO_NOT_AUTO_CLASSIFY,CheckinType.MINOR_VERSION);
                         res.save(RefreshMode.NO_REFRESH);
                    }
               }
          }
          catch (Exception e)
          {
               e.printStackTrace();
               //TODO If caused by VSID issue attempt Delete Here
               logger.error(e.getMessage(), e);
               throw e;
          }
          return res;
     }
          private void addContent(Document dSource, Document d,  String documentTitle, PropertyFilter myFilterId, Id GUID, String DocClass) throws Exception {
               //ADD CONTENT  NEW CODE HERE
               //ASB ... 4 024
               int retryCount = 0;
               Document currDoc = null;
               //while (retryCount < retryLimit){
                    try {
                         currDoc = (Document)os.fetchObject(DocClass, GUID, myFilterId);
                         //Add substituteDoc above to get Content Stream
                         //Document currDoc = (Document)osinput.fetchObject("Document", GUID, myFilterId);
                         com.filenet.api.core.ContentTransfer ct = Factory.ContentTransfer.createInstance();
                         String dClassName = currDoc.getClassName();
                         String dMimeType = currDoc.get_MimeType();
                         String dContentSize = currDoc.get_ContentSize().toString();
                         String dGUID = currDoc.get_Id().toString();
                         InputStream str = currDoc.accessContentStream(0);
                         ct.setCaptureSource(str);
                         // Add Document Title
                         ct.set_RetrievalName(documentTitle);
                         // Add Content Mime Type
                         ct.set_ContentType(currDoc.get_MimeType());
                         ContentElementList cel = Factory.ContentElement.createList();
                         cel.add(ct);
                         d.set_ContentElements(cel);
                         //break;
                         }
                         catch (Exception e){
                              String testMessage = e.getMessage();
                              String testCode = e.toString();
                              if( testMessage.contains("A uniqueness requirement has been violated")){
                                   //retryCount = retryLimit;
                                   return;
                              }
                              //ASB ... 4 Check if the Content could not be added because the document already exists, if so
                              //          Remove document and retry (next run)
                              //  ASB ... 4 025 Log Exception here
                              logger.error(e.getMessage(), e);
                              retryCount ++;
                              //retryTotalProcessedCount ++;
                              //logger.info("Retry Count - createVersion method : " + retryCount + " of " + retryLimit + " On : " + documentTitle + " : with GUID : " + GUID);
                         }
               //} //ASB ... 4 031 Above loops Test retryCount less than the retryLimit of 7
               // Add Security ACL
               //ASB ... 2 020 Update Document ACL from source Document security
               AccessPermissionList  aclIn = null;
                   aclIn = currDoc.get_Permissions();
                   d.set_Permissions(aclIn);
               Boolean isMajorVersion = false;
               if(currDoc.get_MinorVersionNumber() == 0) isMajorVersion = true;
              //ASB ... 4 028 Change to do not Classify from AutoClassify.AUTO_CLASSIFY, to
               if (isMajorVersion){
                    d.checkin(AutoClassify.DO_NOT_AUTO_CLASSIFY,CheckinType.MAJOR_VERSION);
               }
               else {
                    d.checkin(AutoClassify.DO_NOT_AUTO_CLASSIFY,CheckinType.MINOR_VERSION);
               }
               //d.checkin(AutoClassify.AUTO_CLASSIFY, CheckinType.MAJOR_VERSION);
              //ASB ... 4 25 Try/Catch here ?
               Date dateLastModified = dSource.get_DateLastModified();
               //Date dateCreated = dSource.get_DateCreated(); //ASB ... 4 029
               String sModifier = dSource.get_LastModifier();
               Date dateCheckedIn = dSource.get_DateCheckedIn();
               // Update Checkin Date etc from Source here
               //d.set_DateCreated(dateCreated);  //ASB ... 4 029 READONLY AT THIS POINT!!
               d.set_DateLastModified(dateLastModified);
               d.set_LastModifier(sModifier);
               d.set_DateCheckedIn(dateCheckedIn); //ASB ... 2 005
               d.save(RefreshMode.REFRESH); //ASB Only save if Not in Bulk mode 21-08-2022
          }
          //Adds properties such as LastModifier to the document properties.
          //Called by doReservationProperties, createVersion
          private void writeSpecialDocProps(Document d,Iterator iterProps, Boolean justCreated, String documentTitle, Document dInput) throws Exception {
              //ASB ... 2 008 Updated for new property definition objects
               //Retrieve the current property list to retrieve based on the Document Class definitions from the Object Store
               String dClassName     = d.getClassName();
               com.filenet.api.admin.ClassDefinition classDefs = Factory.ClassDefinition.fetchInstance(os,dClassName, null);
               PropertyDefinitionList propList = classDefs.get_PropertyDefinitions();
               StringBuffer tempBuf = new StringBuffer();
               //Retrieve property names
               Iterator iter = propList.iterator();
               while (iter.hasNext())
               {
                    //ASB ... 4 025 Catch any property exceptions here
                    try{
                         PropertyDefinition propDef = (PropertyDefinition) iter.next();
                                   String propName = propDef.get_SymbolicName();
                                   //ASB ... 2 011 Need to skip Date Last Accessed etc - ReadOnly error here
                                   if (!(propName.equalsIgnoreCase("DateContentLastAccessed")
                                             ||propName.equalsIgnoreCase("LockOwner")
                                             ||propName.equalsIgnoreCase("Name")
                                             ||propName.equalsIgnoreCase("StorageLocation")
                                             ||propName.equalsIgnoreCase("ContentElementsPresent")
                                             ||propName.equalsIgnoreCase("DateCheckedIn")
                                             ||propName.equalsIgnoreCase("ContentRetentionDate")
                                             ||propName.equalsIgnoreCase("CurrentState")
                                             ||propName.equalsIgnoreCase("CmThumbnails"))){ //ASB 21-08-2022
//ASB ... 2 011 Set to check cardinality
                                        int CardinalityVal = propDef.get_Cardinality().getValue();
                                        switch (propDef.get_DataType().getValue()){
                                        case StringType:
                                             if (CardinalityVal == CardinalitySINGLE){
                                                  String val = dInput.getProperties().getStringValue(propName);
                                                  d.getProperties().putValue(propName, val);
                                                  if (propName.equalsIgnoreCase("lastmodifier")){
                                                       d.set_LastModifier(val);
                                                  }
                                                  if (!propName.equalsIgnoreCase("creator") && !propName.equalsIgnoreCase("lastmodifier")){
                                                       d.getProperties().putValue(propName, val);
                                                  }
                                                  }else{
                                                       //List
                                                       StringList valList = dInput.getProperties().getStringListValue(propName);
                                                       d.getProperties().putValue(propName, valList);
                                                  }
                                             break;
                                        case DateType:
                                             if (CardinalityVal == CardinalitySINGLE){
                                                  Date dateVal = dInput.getProperties().getDateTimeValue(propName);
                                                  if (propName.equalsIgnoreCase("datelastmodified")){
                                                       if (!justCreated){
                                                            dInput.getProperties().removeFromCache("DateLastModified");
                                                       }
                                                       d.set_DateLastModified(dateVal);
                                                  }
                                                  if (!propName.equalsIgnoreCase("datecreated") && !propName.equalsIgnoreCase("datelastmodified")){
                                                       d.getProperties().putValue(propName, dateVal);
                                                  }
                                             }
                                             //TODO Process Multi-value Dates (if exists)?
                                             break;
                                        default:
                                        }
                                   }
                         }catch(Exception writeProp){
                              logger.error(writeProp.getMessage(), writeProp);
                    }
               }
               //ASB ... 2 020 Update Document ACL from source Document security
               AccessPermissionList  aclIn = null;
                   aclIn = dInput.get_Permissions();
                  d.set_Permissions(aclIn);
          }
          //Sets the properties on the reservation object for docs that were checked out on the source system.
          //Sets properties but no content.  No need to set mime type here - causes problems.
          //private Document doReservationProperties(Node verNode, Document d) throws Exception {
          private Document doReservationProperties(int versCount, Document dInput, Document d, Boolean isReserved, int majorVn,int minorVn, String documentTitle     ) throws Exception{
               com.filenet.api.property.Properties props = Factory.Document.createInstance(os, null).getProperties();
               String sCreator = dInput.get_Creator();
               Date dateCreated = dInput.get_DateCreated();
               String sLastModifier = dInput.get_LastModifier();
               Date dateLastModified = dInput.get_DateLastModified();
               // This collection is used when we do the checkout
               props.putValue("Creator", sCreator);
               props.putValue("DateCreated", dateCreated);
               props.putValue("LastModifier", sLastModifier);
               props.putValue("DateLastModified", dateLastModified);
               Date dateCheckedIn = dInput.get_DateCheckedIn();
               //Update Checkin Date etc from Source here
               d.set_LastModifier(sCreator);
               d.set_DateLastModified(dateCreated);
               d.set_DateCreated(dateCreated); //ASB ... 4 029
               Id reservationId_null = null;
               String reservationClass_null = null; // to remind of the parameter types!
               try
              {
                 // Get Document properties
                    com.filenet.api.property.Properties docProps = d.getProperties();
                   Iterator iterProps = props.iterator();
                  Boolean justCreated = false;
                    writeSpecialDocProps(d, iterProps, justCreated,documentTitle, dInput);
               }
               catch (Exception e){
                    throw new Exception(e.getMessage(), e);
               }
               d.set_LastModifier(sLastModifier);
               // ASB Compare : d.checkout(ReservationType.EXCLUSIVE, GUID, docClass, null);
               d.checkout(ReservationType.OBJECT_STORE_DEFAULT, reservationId_null, reservationClass_null, props);
               d.set_DateLastModified(dateLastModified);
               d.set_DateCreated(dateCreated); //ASB ... 4 029
               d.set_LastModifier(sLastModifier);
              d.save(RefreshMode.REFRESH);
               return d;
          }
          private boolean isClassExist(String className) throws Exception {
               if (classNames.containsKey(className))
                    return true;
               try {
                    com.filenet.api.admin.ClassDefinition test = Factory.ClassDefinition.fetchInstance(os, className, null);
                    classNames.put(test.get_Name(), test.get_Name());
                    return true;
               }
               catch (EngineRuntimeException e){
                    if (e.getExceptionCode() == ExceptionCode.E_BAD_CLASSID)
                         return false;
                    throw e;
               }
          }
        private void updateNewDocument(Document doc,String docTitle, String mimeType, String FileName) {
             // REF
             // Page 92-93 of http://www.redbooks.ibm.com/redbooks/pdfs/sg247743.pdf
             //
             // Get the document (saving a round-trip that a fetch would require)
             //Document doc = Factory.Document.getInstance(os,"Document","/Doc1");
             // Checkout the document and save
             doc.checkout(ReservationType.EXCLUSIVE, null, null, null);
             doc.save(RefreshMode.REFRESH);
             // Get the reservation object
             Document res = (Document)doc.get_Reservation();
             // Update the properties
             res.getProperties().putValue("DocumentTitle",docTitle);
             // Prepare the content for attaching
             // Create the element list
             ContentElementList list = Factory.ContentElement.createList();
             // Create a content transfer element by attaching a simple text file
             com.filenet.api.core.ContentTransfer element = Factory.ContentTransfer.createInstance();
             // Set the MIME type
             element.set_ContentType(mimeType);
             // Set the retrieval name
             element.set_RetrievalName(FileName);
             // Set the content source
             try {
                    element.setCaptureSource(new FileInputStream(new File(FileName)));
               } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
               }
             // Add the item to the list
             list.add(element);
             // Add the content transfer list to the document
             res.set_ContentElements(list);
             // Set the PendingAction to be check-in as a major version without
             // automatic content classification
             res.checkin(AutoClassify.DO_NOT_AUTO_CLASSIFY,
             CheckinType.MAJOR_VERSION);
             // Save the document to the repository
             res.save(RefreshMode.NO_REFRESH);
        }
        private static void addQTitlePage( com.lowagie.text.Document document)
                   throws DocumentException, IOException {
                   // Add the Audit Master Logo
                   // Creating an ImageData object
                   String imageLogoFile = "/opt/AuditReport/images/AuditMasterLogo.tif"; //TODO add to config.xml file
                   RandomAccessFileOrArray ra = new RandomAccessFileOrArray(imageLogoFile);
                   int pages = TiffImage.getNumberOfPages(ra);
                   // Creating an Image object
                   Image image;
                   image = TiffImage.getTiffImage(ra, 1);
                   // Adding the Audit Master Logo image to the Audit report document
                   Rectangle pageSize = new Rectangle(image.getWidth(),
                             image.getHeight());
                   document.setPageSize(pageSize);
                   document.add(image);
                   // Start a new page
                   document.newPage();
                 Paragraph preface = new Paragraph();
                 // We add one empty line
                 addEmptyLine(preface, 1);
                 // Lets write a big header
                 //TODO Pick all text up from the config.xml file
                 Paragraph paragraph = new Paragraph("Audit Master - Audit Report", catFont);
                 paragraph.setAlignment(Element.ALIGN_CENTER);
                 preface.add(paragraph);
                    addEmptyLine(preface, 1);
                 // Lets write another big header
                 //TODO Pick all text up from the config.xml file
                    paragraph = new Paragraph("ASB Software Development Limited", catFont);
                    paragraph.setAlignment(Element.ALIGN_CENTER);
                 preface.add(paragraph);
                    addEmptyLine(preface, 1);
                 // Lets write another big header
                 //TODO Pick all text up from the config.xml file
                    paragraph = new Paragraph(sTab+ "Department Audit Process Task", catFont);
                    paragraph.setAlignment(Element.ALIGN_CENTER);
                 preface.add(paragraph);
                 // Lets write another big header
                 //TODO Pick all text up from the config.xml file
                    paragraph = new Paragraph(sTab+ "ISO9000/BS5750", catFont);
                    paragraph.setAlignment(Element.ALIGN_CENTER);
                 preface.add(paragraph);
                 addEmptyLine(preface, 1);
                 // Will create: Report generated by: _name, _date
                 preface.add(new Paragraph(sTab+ "Report generated by: " + System.getProperty("user.name") + ", " + new Date(), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                     smallBold));
                 addEmptyLine(preface, 3);
                 paragraph = new Paragraph(sTab+ "Note: This is an automatically generated Audit Report, the full Audit results and conclusions are produced following the run of the review tasks. ",
                         smallBold);
                paragraph.setAlignment(Element.ALIGN_LEFT);
                paragraph.setIndentationLeft(50);
                 preface.add(paragraph);
                 addEmptyLine(preface, 8);
                 addEmptyLine(preface, 8);
                 paragraph = new Paragraph(sTab+ "Once you have completed the initial Department Audit, you should have: " +
                           " " + sTab +
                           "•      Arranged a non-conformance review and a follow-up audit and questions. " +
                           " " + sTab +
                           "•     Allowed the Department sufficient time to correct the non-conformances and   " +
                           " " + sTab+
                           "•     Generated the evidence for the required changes to their procedures.   " +
                           "",
                     redFont);
                paragraph.setAlignment(Element.ALIGN_LEFT);
                paragraph.setIndentationLeft(50);
                 preface.add(paragraph);
                 document.add(preface);
                 // Start a new page
                 document.newPage();
               }
        private static void addEmptyLine(Paragraph paragraph, int number) {
            for (int i = 0; i < number; i++) {
              paragraph.add(new Paragraph(" "));
            }
          }
        private static void  getTaskDepartmentPropsSections(CmTask object,String taskClass,com.filenet.api.admin.ClassDefinition tClassDefs, PropertyDefinitionList tPropList,com.filenet.api.property.Properties props, Iterator iterProps, String [][] Departmentprops, String [] TabSECTIONS_Department, String [][] Department_propValues,String [][] Department_propNames,Integer noDepartmentSections, Integer [] noDepartmentprops)throws Exception {
            // called as :
             //                getTaskDepartmentPropsSections(currentTask, taskClass,tClassDefs,tPropList,props,iterProps,Department_props,TabSECTIONS_Department,Department_propValues,noDepartmentSections,noDepartmentprops);
              //    String [][] Department_props paramCount is the section number, 0-6 i is the noDepartmentprops[paramCount] contains the symbolic property name
            //    TabSECTIONS_Department[]     // has noDepartmentSections
            //    Integer noDepartmentSections  The number of sections to process 0-3
                   //ASB
                   //Retrieve the current property list to retrieve based on the Task Class definitions from the Object Store
            String tClassName = object.getClassName(); //ASB 15-08-2022
            com.filenet.api.admin.ClassDefinition classDefs = tClassDefs;
            PropertyDefinitionList propList = tPropList;
            StringBuffer tempBuf = new StringBuffer();
            //Retrieve property names code From writeDocProps to compare
            Iterator iter =propList.iterator();
            while (iter.hasNext()){
                 try {
                      PropertyDefinition propDef = (PropertyDefinition) iter.next();
                      String propName = propDef.get_SymbolicName();
                      String propDisplayName = propDef.get_DisplayName();
                      //ASB ... 2 011 Need to skip Date Last Accessed etc - ReadOnly error here
                      if (!(propName.equalsIgnoreCase("Name")
                              ||propName.equalsIgnoreCase("DateCheckedIn")
                              ||propName.equalsIgnoreCase("CurrentState")
                              ||propName.equalsIgnoreCase("PathName")
                              ||propName.equalsIgnoreCase("LockOwner")
                              ||propName.equalsIgnoreCase("Owner") //ASB 15-08-2022
                              ||propName.startsWith("CmAcm") )){
                              //ASB ... Set to check property cardinality
                              int CardinalityVal = propDef.get_Cardinality().getValue();
                              int propIndex;
                              //===============START
                                  switch (propDef.get_DataType().getValue()){
                               case StringType:
                                    if (CardinalityVal == CardinalitySINGLE){
                                        String val = object.getProperties().getStringValue(propName);
                                        for (int sectionIndex = 0; sectionIndex < noDepartmentSections; sectionIndex++) {
                                               for (propIndex = 0; propIndex < noDepartmentprops[sectionIndex]; propIndex++) { //Department_propValues
                                             // Value for Required response
                                                  String propValue = val;
                                                  String propTest = Departmentprops[sectionIndex][propIndex];
                                                 if (propName.equalsIgnoreCase(propTest)){
                                                         Department_propValues[sectionIndex][propIndex] =propValue;
                                                         Department_propNames[sectionIndex][propIndex] = propDisplayName;
                                                   }
                                                }
                                          }
                                         //ASB 15-08-2022
                                  }else{
                                         StringList valList = object.getProperties().getStringListValue(propName);
                                         //Iterator itList = valList.iterator();
                                         //String valLists = " ";
                                         //while (itList.hasNext()){
                                            //  valLists = valLists +" " + sTab + itList.toString();
                                         //}
                                          for (int sectionIndex = 0; sectionIndex < noDepartmentSections; sectionIndex++){
                                                     for (propIndex = 0; propIndex < noDepartmentprops[sectionIndex]; propIndex++) { //Department_propValues
                                                 // Value for Required response
                                                     if (propName.equalsIgnoreCase(Department_props[sectionIndex][propIndex])){
                                                         // Iterate through the list of values
                                                         StringBuffer sb = new StringBuffer();
                                                         for( Iterator i = valList.iterator(); i.hasNext(); )
                                                         {
                                                         // Get the value and cast to an Id property type
                                                         String propertyVal = (String)i.next();
                                                         sb.append(propertyVal + " ");
                                                         }
                                                             Department_propValues[sectionIndex][propIndex] =sb.toString();
                                                               Department_propNames[sectionIndex][propIndex] = propDisplayName;
                                                       }
                                                     }
                                              }
                                       }
                                         // Process Multi-value Strings (if required)?
                                         break;
                                    case DateType:
                                         if (CardinalityVal == CardinalitySINGLE){
                                              Date dateVal = object.getProperties().getDateTimeValue(propName);
                                             for (int sectionIndex = 0; sectionIndex < noDepartmentSections; sectionIndex++){
                                                          for (propIndex = 0; propIndex < noDepartmentprops[sectionIndex]; propIndex++){ //Department_propValues
                                                 // Value for Required response
                                                         if (propName.equalsIgnoreCase(Departmentprops[sectionIndex][propIndex])){
                                                              if(dateVal != null) {
                                       Department_propValues[sectionIndex][propIndex] =dateVal.toString();
                                       Department_propNames[sectionIndex][propIndex] = propDisplayName;
                                                              }
                                                         }
                                                          }
                                                }
                                         }else{
                                               //
                                          }
                                      break;
                                     default:
                           }
                              //===============END
                         }
                       }catch (Exception writeProp){
                         logger.error(writeProp.getMessage(), writeProp);
                    }
            }
        }
        private static void  getTaskAuditPropsSections(CmTask object,String taskClass,com.filenet.api.admin.ClassDefinition tClassDefs, PropertyDefinitionList tPropList,com.filenet.api.property.Properties props, Iterator iterProps, String [][] Auditprops, String [] TabSECTIONS_Audit, String [][] Audit_propValues,String [][] Audit_propNames, Integer noAuditSections, Integer [] noAuditprops)throws Exception {
            // called as :
             //                getTaskAuditPropsSections(currentTask, taskClass,tClassDefs,tPropList,props,iterProps,Audit_props,TabSECTIONS_Audit,Audit_propValues,noAuditSections,noAuditprops);
             //    String [][] Audit_props paramCount is the section number, 0-6 i is the noAuditprops[paramCount] contains the symbolic property name
            //    TabSECTIONS_Audit[]     // has noAuditSections
            //    Integer noAuditSections  The number of sections to process 0-3
                   //ASB
                   //Retrieve the current property list to retrieve based on the Task Class definitions from the Object Store
            String tClassName = object.getClassName(); //ASB 15-08-2022
            com.filenet.api.admin.ClassDefinition classDefs = tClassDefs;
            PropertyDefinitionList propList = tPropList;
            StringBuffer tempBuf = new StringBuffer();
            //Retrieve property names code From writeDocProps to compare
            Iterator iter =propList.iterator();
            while (iter.hasNext()){
                 try {
                      PropertyDefinition propDef = (PropertyDefinition) iter.next();
                      String propName = propDef.get_SymbolicName();
                      String propDisplayName = propDef.get_DisplayName();
                      //ASB ... 2 011 Need to skip Date Last Accessed etc - ReadOnly error here
                      if (!(propName.equalsIgnoreCase("Name")
                              ||propName.equalsIgnoreCase("DateCheckedIn")
                              ||propName.equalsIgnoreCase("CurrentState")
                              ||propName.equalsIgnoreCase("PathName")
                              ||propName.equalsIgnoreCase("LockOwner")
                              ||propName.equalsIgnoreCase("Owner") //ASB 15-08-2022
                              ||propName.startsWith("CmAcm") )){
                              //ASB ... Set to check property cardinality
                              int CardinalityVal = propDef.get_Cardinality().getValue();
                              int propIndex;
                              //===============START
                                  switch (propDef.get_DataType().getValue()){
                               case StringType:
                                    if (CardinalityVal == CardinalitySINGLE){
                                        String val = object.getProperties().getStringValue(propName);
                                        for (int sectionIndex = 0; sectionIndex < noAuditSections; sectionIndex++) {
                                               for (propIndex = 0; propIndex < noAuditprops[sectionIndex]; propIndex++) { //Audit_propValues
                                             // Value for Required response
                                                  String propValue = val;
                                                  String propTest = Auditprops[sectionIndex][propIndex];
                                                 if (propName.equalsIgnoreCase(propTest)){
                                                         Audit_propValues[sectionIndex][propIndex] =propValue;
                                                         Audit_propNames[sectionIndex][propIndex] = propDisplayName;
                                                   }
                                                }
                                          }
                                         //ASB 15-08-2022
                                  }else{
                                         StringList valList = object.getProperties().getStringListValue(propName);
                                       // Get a multi-value String property
                                         //Iterator itList = valList.iterator();
                                         //String valLists = " ";
                                         //while (itList.hasNext()){
                                        //  valLists = valLists +" " + sTab + itList.toString();
                                        // }
                                          for (int sectionIndex = 0; sectionIndex < noAuditSections; sectionIndex++){
                                                     for (propIndex = 0; propIndex < noAuditprops[sectionIndex]; propIndex++) { //Audit_propValues
                                                 // Value for Required response
                                                     if (propName.equalsIgnoreCase(Audit_props[sectionIndex][propIndex])){
                                                           // Iterate through the list of values
                                                           StringBuffer sb = new StringBuffer();
                                                           for( Iterator i = valList.iterator(); i.hasNext(); )
                                                           {
                                                           // Get the value and cast to an Id property type
                                                           String propertyVal = (String)i.next();
                                                           sb.append(propertyVal + " ");
                                                           }
                                                              Audit_propValues[sectionIndex][propIndex] =sb.toString();
                                                               Audit_propNames[sectionIndex][propIndex] = propDisplayName;
                                                       }
                                                     }
                                              }
                                       }
                                         // Process Multi-value Strings (if required)?
                                         break;
                                    case DateType:
                                         if (CardinalityVal == CardinalitySINGLE){
                                              Date dateVal = object.getProperties().getDateTimeValue(propName);
                                             for (int sectionIndex = 0; sectionIndex < noAuditSections; sectionIndex++){
                                                          for (propIndex = 0; propIndex < noAuditprops[sectionIndex]; propIndex++){ //Audit_propValues
                                                 // Value for Required response
                                                         if (propName.equalsIgnoreCase(Auditprops[sectionIndex][propIndex])){
                                                              if(dateVal != null) {
                                                                     Audit_propValues[sectionIndex][propIndex] =dateVal.toString();
                                                                     Audit_propNames[sectionIndex][propIndex] = propDisplayName;
                                                              }
                                                         }
                                                          }
                                                }
                                         }else{
                                               //
                                          }
                                      break;
                                     default:
                           }
                              //===============END
                         }
                       }catch (Exception writeProp){
                         logger.error(writeProp.getMessage(), writeProp);
                    }
            }
        }
        private static void  getTaskAUDITORPropsSections(CmTask object,String taskClass,com.filenet.api.admin.ClassDefinition tClassDefs, PropertyDefinitionList tPropList,com.filenet.api.property.Properties props, Iterator iterProps, String [][] AUDITORprops, String [] TabSECTIONS_AUDITOR, String [][] AUDITOR_propValues,String [][] AUDITOR_propNames,Integer noAUDITORSections, Integer [] noAUDITORprops)throws Exception {
            // called as :
             //                getTaskAUDITORPropsSections(currentTask, taskClass,tClassDefs,tPropList,props,iterProps,AUDITOR_props,TabSECTIONS_AUDITOR,AUDITOR_propValues,noAUDITORSections,noAUDITORprops);
              //    String [][] AUDITOR_props paramCount is the section number, 0-6 i is the noAUDITORprops[paramCount] contains the symbolic property name
            //    TabSECTIONS_AUDITOR[]     // has noAUDITORSections
            //    Integer noAUDITORSections  The number of sections to process 0-3
                   //ASB
                   //Retrieve the current property list to retrieve based on the Task Class definitions from the Object Store
            String tClassName = object.getClassName(); //ASB 15-08-2022
            com.filenet.api.admin.ClassDefinition classDefs = tClassDefs;
            PropertyDefinitionList propList = tPropList;
            StringBuffer tempBuf = new StringBuffer();
            //Retrieve property names code From writeDocProps to compare
            Iterator iter =propList.iterator();
            while (iter.hasNext()){
                 try {
                      PropertyDefinition propDef = (PropertyDefinition) iter.next();
                      String propName = propDef.get_SymbolicName();
                      String propDisplayName = propDef.get_DisplayName();
                      //ASB ... 2 011 Need to skip Date Last Accessed etc - ReadOnly error here
                      if (!(propName.equalsIgnoreCase("Name")
                              ||propName.equalsIgnoreCase("DateCheckedIn")
                              ||propName.equalsIgnoreCase("CurrentState")
                              ||propName.equalsIgnoreCase("PathName")
                              ||propName.equalsIgnoreCase("LockOwner")
                              ||propName.equalsIgnoreCase("Owner") //ASB 15-08-2022
                              ||propName.startsWith("CmAcm") )){
                              //ASB ... Set to check property cardinality
                              int CardinalityVal = propDef.get_Cardinality().getValue();
                              int propIndex;
                              //===============START
                                  switch (propDef.get_DataType().getValue()){
                               case StringType:
                                    if (CardinalityVal == CardinalitySINGLE){
                                        String val = object.getProperties().getStringValue(propName);
                                        for (int sectionIndex = 0; sectionIndex < noAUDITORSections; sectionIndex++) {
                                               for (propIndex = 0; propIndex < noAUDITORprops[sectionIndex]; propIndex++) { //AUDITOR_propValues
                                             // Value for Required response
                                                  String propValue = val;
                                                  String propTest = AUDITORprops[sectionIndex][propIndex];
                                                 if (propName.equalsIgnoreCase(propTest)){
                                                         AUDITOR_propValues[sectionIndex][propIndex] =propValue;
                                                         AUDITOR_propNames[sectionIndex][propIndex] = propDisplayName;
                                                   }
                                                }
                                          }
                                         //ASB 15-08-2022
                                  }else{
                                         StringList valList = object.getProperties().getStringListValue(propName);
                                         //Iterator itList = valList.iterator();
                                         //String valLists = " ";
                                         //while (itList.hasNext()){
                                            //  valLists = valLists +" " + sTab + itList.toString();
                                         //}
                                          for (int sectionIndex = 0; sectionIndex < noAUDITORSections; sectionIndex++){
                                                     for (propIndex = 0; propIndex < noAUDITORprops[sectionIndex]; propIndex++) { //AUDITOR_propValues
                                                 // Value for Required response
                                                     if (propName.equalsIgnoreCase(AUDITOR_props[sectionIndex][propIndex])){
                                                         // Iterate through the list of values
                                                         StringBuffer sb = new StringBuffer();
                                                         for( Iterator i = valList.iterator(); i.hasNext(); )
                                                         {
                                                         // Get the value and cast to an Id property type
                                                         String propertyVal = (String)i.next();
                                                         sb.append(propertyVal + " ");
                                                         }
                                                             AUDITOR_propValues[sectionIndex][propIndex] =sb.toString();
                                                               AUDITOR_propNames[sectionIndex][propIndex] = propDisplayName;
                                                       }
                                                     }
                                              }
                                       }
                                         // Process Multi-value Strings (if required)?
                                         break;
                                case DateType:
                                     if (CardinalityVal == CardinalitySINGLE){
                                          Date dateVal = object.getProperties().getDateTimeValue(propName);
                                         for (int sectionIndex = 0; sectionIndex < noAUDITORSections; sectionIndex++){
                                                      for (propIndex = 0; propIndex < noAUDITORprops[sectionIndex]; propIndex++){ //AUDITOR_propValues
                                             // Value for Required response
                                                     if (propName.equalsIgnoreCase(AUDITORprops[sectionIndex][propIndex])){
                                                          if(dateVal != null) {
                                                                  AUDITOR_propValues[sectionIndex][propIndex] =dateVal.toString();
                                                                 AUDITOR_propNames[sectionIndex][propIndex] = propDisplayName;
                                                          }
                                                     }
                                                      }
                                            }
                                         }else{
                                               //
                                          }
                                      break;
                                     //      /*
                                     case DoubleType:
                                          if (CardinalityVal == CardinalitySINGLE){
                                               Double doubleVal = object.getProperties().getFloat64Value(propName);
                                              for (int sectionIndex = 0; sectionIndex < noAUDITORSections; sectionIndex++){
                                                           for (propIndex = 0; propIndex < noAUDITORprops[sectionIndex]; propIndex++){ //AUDITOR_propValues
                                                  // Value for Required response
                                                          if (propName.equalsIgnoreCase(AUDITORprops[sectionIndex][propIndex])){
                                                               if(doubleVal != null) {
                                                                   AUDITOR_propValues[sectionIndex][propIndex] =doubleVal.toString();
                                                                   AUDITOR_propNames[sectionIndex][propIndex] = propDisplayName;
                                                               }
                                                          }
                                                           }
                                                 }
                                          }else{
                                                //
                                           }
                                         //  */
                                     default:
                           }
                              //===============END
                         }
                       }catch (Exception writeProp){
                         logger.error(writeProp.getMessage(), writeProp);
                    }
            }
        }
        private static void addAUDITORTitlePage( com.lowagie.text.Document document)
                   throws DocumentException, IOException {
                  // Add the Audit Master Logo
                  // Creating an ImageData object
                  String imageLogoFile = "/opt/AuditReport/images/AuditMasterLogo.tif"; //TODO add to config.xml file
                  RandomAccessFileOrArray ra = new RandomAccessFileOrArray(imageLogoFile);
                  int pages = TiffImage.getNumberOfPages(ra);
                  // Creating an Image object
                  Image image;
                  image = TiffImage.getTiffImage(ra, 1);
                  // Adding the Audit Master Logo image to the Audit report document
                  Rectangle pageSize = new Rectangle(image.getWidth(),
                  image.getHeight());
                  document.setPageSize(pageSize);
                  document.add(image);
                 // Start a new page
                 document.newPage();
                 Paragraph preface = new Paragraph();
                 // We add one empty line
                 addEmptyLine(preface, 1);
                 // Lets write a big header
                 //TODO Pick all text up from the config.xml file
                 Paragraph paragraph = new Paragraph("Audit Master - Audit Report", catFont);
                 paragraph.setAlignment(Element.ALIGN_CENTER);
                 preface.add(paragraph);
                    addEmptyLine(preface, 1);
                 // Lets write another big header
                 //TODO Pick all text up from the config.xml file
                    paragraph = new Paragraph("ASB Software Development Limited", catFont);
                    paragraph.setAlignment(Element.ALIGN_CENTER);
                     preface.add(paragraph);
                    addEmptyLine(preface, 1);
                   paragraph = new Paragraph("Department Audit Process Task", catFont);
                   paragraph.setAlignment(Element.ALIGN_CENTER);
                   preface.add(paragraph);
                      addEmptyLine(preface, 1);
                 // Lets write another big header
                 //TODO Pick all text up from the config.xml file
                    paragraph = new Paragraph(sTab+ "ISO9000/BS5750", catFont);
                    paragraph.setAlignment(Element.ALIGN_CENTER);
                 preface.add(paragraph);
                 addEmptyLine(preface, 1);
                 // Will create: Report generated by: _name, _date
                 preface.add(new Paragraph(sTab+ "Report generated by: " + System.getProperty("user.name") + ", " + new Date(), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                     smallBold));
                 addEmptyLine(preface, 3);
                 paragraph = new Paragraph(sTab+ "Note: This is an automatically generated Audit Report, the full Audit results and conclusions are produced following the run of the review tasks. ",
                         smallBold);
                paragraph.setAlignment(Element.ALIGN_LEFT);
                paragraph.setIndentationLeft(50);
                 preface.add(paragraph);
                 addEmptyLine(preface, 8);
                 paragraph = new Paragraph(sTab+ "Once you have completed the initial Department Audit, you should have: " +
                           " " + sTab +
                           "•      Arranged a non-conformance review and a follow-up audit and questions. " +
                           " " + sTab +
                           "•     Allowed the Department sufficient time to correct the non-conformances and   " +
                           " " + sTab+
                           "•     Generated the evidence for the required changes to their procedures.   " +
                           "",
                     redFont);
                paragraph.setAlignment(Element.ALIGN_LEFT);
                paragraph.setIndentationLeft(50);
                 preface.add(paragraph);
                 document.add(preface);
                 // Start a new page
                 document.newPage();
               }
     //=========E N D    C O D E    F O R    P D F   I M P O R T ==========
     }
Listing 6-18

The QOperations.java source code file

Part 3 – Supporting Java Classes for the Main Audit Report Program

The supporting Java methods called by the main Audit Report program are described in this part.

Adding the EmailTemplate Utility Java Class

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, email template, and the finish button is selected.

Figure 6-22

The EmailTemplate class code is added to the package

The Java code for the EmailTemplate.java source is as follows.
package com.ibm.filenet.ps.ciops;
import java.util.Date;
import java.util.Iterator;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import com.filenet.api.collection.ContentElementList;
import com.filenet.api.constants.PropertyNames;
import com.filenet.api.core.ContentTransfer;
import com.filenet.api.core.Document;
import filenet.vw.api.VWAttachment;
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions.  IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you “AS IS “
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
/**
 * Utility class for the creation of e-mail messages. See the method
 * {@link mypackage.MyOperations#sendMailMessage(VWAttachment, String, String, String, String[], String[]) sendMailMessage()} for
 * a description of the features.
 *
 * @author Ricardo Belfor
 *
 */
public class EmailTemplate {
     private Document document;
     public EmailTemplate(Document document) {
          this.document = document;
          this.document.fetchProperties( new String[] { PropertyNames.CONTENT_ELEMENTS, PropertyNames.CONTENT_TYPE, PropertyNames.RETRIEVAL_NAME } );
     }
     /**
      * Creates an e-mail message based using the document used to create this class
      * as a template.
      *
      * @param mailSession the mail session
      * @param from the sender of the message
      * @param recipients the recipients of the message
      * @param subject the subject of the message
      * @param templateNames the names of the template parameters.
      * @param templateValues the values of the template parameters.
      *
      * @return the message
      *
      * @throws Exception
      */
     public MimeMessage getMessage(Session mailSession, InternetAddress from, InternetAddress[] recipients,
               String subject, String[] templateNames, String templateValues[]) throws Exception {
        MimeMessage mimeMessage = createMimeMessage(mailSession, from, recipients, subject);
        Multipart multiPart = new MimeMultipart();
        addMimeBodyParts(multiPart, templateNames, templateValues );
        mimeMessage.setContent( multiPart );
        return mimeMessage;
     }
     private void addMimeBodyParts(Multipart multiPart, String[] templateNames, String templateValues[] ) throws MessagingException {
          ContentElementList contentElements = document.get_ContentElements();
          Iterator<?> iterator = contentElements.iterator();
          if ( !iterator.hasNext() ) {
               return;
          }
          ContentTransfer contentTransfer = (ContentTransfer) iterator.next();
          addMessagePart(multiPart, templateNames, templateValues, contentTransfer);
          addImageParts(multiPart, iterator);
     }
     private void addMessagePart(Multipart multiPart, String[] templateNames, String[] templateValues,
               ContentTransfer contentTransfer) throws MessagingException {
          MimeBodyPart mimeBodyPart = new MimeBodyPart();
          DataSource dataSource = new TemplateContentElementDataSource(contentTransfer, templateNames, templateValues);
          mimeBodyPart.setDataHandler(new DataHandler(dataSource ) );
          multiPart.addBodyPart( mimeBodyPart );
     }
     private void addImageParts(Multipart multiPart, Iterator<?> iterator) throws MessagingException {
          int index = 1;
          while (iterator.hasNext() ) {
               ContentTransfer contentTransfer = (ContentTransfer) iterator.next();
               addImagePart(multiPart, contentTransfer, index);
               ++index;
          }
     }
     private void addImagePart(Multipart multiPart, ContentTransfer contentTransfer, int index)
               throws MessagingException {
          MimeBodyPart imagePart = new MimeBodyPart();
          DataSource dataSource = new ContentElementDataSource( contentTransfer );
          imagePart.setDataHandler(new DataHandler( dataSource) );
          imagePart.setHeader("Content-ID","<content" + index + ">");
          multiPart.addBodyPart( imagePart );
     }
     private MimeMessage createMimeMessage(Session mailSession, InternetAddress from, InternetAddress[] recipients,
               String subject)
               throws MessagingException {
          MimeMessage message = new MimeMessage(mailSession);
        message.setFrom(from);
        message.setRecipients(javax.mail.Message.RecipientType.TO, recipients );
          message.setSubject(subject);
        message.setSentDate(new Date());
          return message;
     }
}
Listing 6-19

The EmailTemplate.java source code file

Adding the TemplateContentElementDataSource Java Class

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, template content element data source, and the finish button is selected.

Figure 6-23

The TemplateContentElementDataSource class code is added to the package

The Java code for the TemplateContentElementDataSource.java source is as follows.
package com.ibm.filenet.ps.ciops;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import javax.activation.DataSource;
import com.filenet.api.core.ContentTransfer;
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions.  IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you “AS IS “
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
/**
 * This class wraps a ContentTransfer object in a DataSource object and
 * substitutes the template place holders with template values. This way the
 * content is uses a part of an e-mail message.
 *
 * @author Ricardo Belfor
 *
 */
public class TemplateContentElementDataSource implements DataSource {
     private ContentTransfer contentTransfer;
     private String[] templateNames;
     private String[] templateValues;
     public TemplateContentElementDataSource(ContentTransfer contentTransfer, String[] templateNames, String templateValues[]) {
          this.contentTransfer = contentTransfer;
          this.templateNames = templateNames;
          this.templateValues = templateValues;
     }
     public String getContentType() {
          return contentTransfer.get_ContentType();
     }
     public InputStream getInputStream() throws IOException {
          return new ByteArrayInputStream( getInputString().getBytes("UTF-8") );
     }
     private String getInputString() throws IOException {
          String inputString = getContentAsString();
          for (int i = 0; i < templateNames.length; i++) {
               String value = getTemplateValue(i);
               inputString = inputString.replaceAll( "\$" + templateNames[i], value );
          }
          return inputString;
     }
     public String getName() {
          return contentTransfer.get_RetrievalName();
     }
     public OutputStream getOutputStream() throws IOException {
          return null;
     }
     private String getTemplateValue(int index) {
          String value = "";
          if (index < templateValues.length && templateValues[index] != null) {
               value = templateValues[index];
          }
          return value;
     }
     private String getContentAsString() throws IOException {
          StringBuffer stringContent = new StringBuffer();
          byte buffer[] = new byte[128];
          InputStream inputStream = contentTransfer.accessContentStream();
          int readCount = 0;
          while ((readCount = inputStream.read(buffer)) > 0) {
               stringContent.append(new String(buffer, 0, readCount));
          }
          return stringContent.toString();
     }
}
Listing 6-20

The TemplateContentElementDataSource.java source code

Adding the com.ibm.filenet.ps.ciops.database Package

A screenshot of a new java package window. It displays the options to create a new java package, where the name is given as, com dot i b m dot file net dot p s dot c i o p s dot database.

Figure 6-24

The com.ibm.filenet.ps.ciops.database package is added

The com.ibm.filenet.ps.ciops.database package is part of the example IBM FileNet Component Integration Workflow Operations code.

Adding the DatabaseLoginModule Java Class

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, database login module, and the finish button is selected.

Figure 6-25

The DatabaseLoginModule class code is added to the com.ibm.filenet.ps.ciops.database package

The Java code for the DatabaseLoginModule.java source is as follows.
package com.ibm.filenet.ps.ciops.database;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import org.apache.log4j.Logger;
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions.  IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you “AS IS “
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
/**
 *
 * @author Ricardo Belfor
 *
 */
public class DatabaseLoginModule implements LoginModule
{
     private static Logger logger = Logger.getLogger( DatabaseLoginModule.class );
     @SuppressWarnings("unchecked")
     private Map sharedState;
     private Subject subject;
    private boolean succeeded;
     private String username;
     private String password;
     private String driverClass;
     private String connectionUrl;
     private Connection connection;
     private DatabasePrincipal principal;
     private boolean commitSucceeded = false;
     private boolean debug = true;
     public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
               Map<String, ?> options)
     {
          this.subject = subject;
          this.sharedState = sharedState;
          driverClass = (String) options.get( "driverClass");
          connectionUrl = (String) options.get( "connectionUrl" );
          debug = "true".equals( options.get("debug") );
          debug( "driverClass: " + driverClass );
          debug( "connectionUrl: " + connectionUrl );
     }
     public boolean login() throws LoginException {
          debug( "[enter] login()" );
          succeeded = false;
          getCredentials();
          createDatabaseConnection();
        succeeded = true;
          debug( "[exit]  login()" );
          return succeeded;
     }
     private void getCredentials() throws LoginException
     {
          username = (String) sharedState.get("javax.security.auth.login.name");
          password = (String) sharedState.get("javax.security.auth.login.password");
        if ( username == null || password == null ) {
             throw new LoginException( "Username or password not set" );
        }
        debug( "username: " + username );
          debug( "password: " + password );
     }
     private Connection createDatabaseConnection() throws LoginException
     {
          try {
               Class.forName( driverClass );
               connection = DriverManager.getConnection(connectionUrl, username, password);
               return connection;
          } catch (Exception exception ) {
               debug( exception.toString(), exception );
               throw new LoginException( exception.getLocalizedMessage() );
          }
     }
     public boolean commit() throws LoginException
     {
          debug( "[enter] commit()" );
        if ( succeeded ) {
              checkSubject();
            registerPrincipal();
            clearCredentials();
            commitSucceeded = true;
              debug( "[exit] commit() (true)" );
            return true;
        }
          debug( "[exit] commit() (false)" );
        return false;
     }
     private void registerPrincipal() throws LoginException {
          debug( "[enter] registerPrincipal" );
          principal = new DatabasePrincipal( username, connection );
          if( !subject.getPrincipals().contains(principal) ) {
              subject.getPrincipals().add(principal);
               debug( principal.toString() +  " added" );
          }
          debug( "[exit]  registerPrincipal" );
     }
     private void checkSubject() throws LoginException
     {
          if ( subject == null ) {
               throw new LoginException("Subject is null");
          }
          if (subject.isReadOnly()) {
            throw new LoginException ("Subject is Readonly");
        }
     }
     public boolean abort() throws LoginException
     {
          debug( "[enter] abort()" );
         if( !succeeded ) return false;
         if( !commitSucceeded ) {
            succeeded = false;
            clearCredentials();
            closeDatabaseConnection();
        } else {
            logout();
        }
          debug( "[exit]  abort()" );
        return true;
     }
     private void closeDatabaseConnection()
     {
          debug( "[enter] closeDatabaseConnection()()" );
          try {
               connection.close();
          } catch (SQLException e) {
               debug( "close failed.", e );
          }
          connection = null;
          debug( "[exit]  closeDatabaseConnection()()" );
     }
     public boolean logout() throws LoginException
     {
          debug( "[enter] logout()" );
        unregisterPrincipal();
        succeeded = commitSucceeded;
        clearCredentials();
        closeDatabaseConnection();
          debug( "[exit]  logout()" );
          return true;
     }
     private void clearCredentials()
     {
          username = null;
          password = null;
     }
     private void unregisterPrincipal()
     {
         if( commitSucceeded ) {
              subject.getPrincipals().remove(principal);
         }
        principal = null;
     }
     private void debug(String message, Throwable throwable) {
          if ( ! debug ) return;
          if ( throwable == null ) {
               logger.debug( message );
          } else {
               logger.error( message, throwable );
          }
     }
     private void debug(String message) {
          debug(message, null );
     }
}
Listing 6-21

The DatabaseLoginModule.java source code file

Adding the DatabasePrincipal Java Class

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, database principal, and the finish button is selected.

Figure 6-26

The DatabasePrincipal class code is added to the com.ibm.filenet.ps.ciops.database package

The Java code for the DatabasePrincipal.java source is as follows.
package com.ibm.filenet.ps.ciops.database;
import java.io.Serializable;
import java.security.Principal;
import java.sql.Connection;
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions.  IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you “AS IS “
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
/**
 *
 * @author Ricardo Belfor
 *
 */
public class DatabasePrincipal implements Principal, Serializable
{
     private static final long serialVersionUID = -6417436232744816471L;
     private String name;
     private Connection connection;
     public DatabasePrincipal(String username, Connection connection2)
     {
          this.name = username;
          this.connection = connection2;
     }
     public String getName()
     {
          return name;
     }
     public Connection getConnection()
     {
          return connection;
     }
     @Override
     public boolean equals(Object object)
     {
        if (object != null )
        {
             if (this == object) return true;
             if (object instanceof DatabasePrincipal)
             {
                 return (((DatabasePrincipal) object).getName().equals(name));
             }
        }
        return false;
     }
     @Override
     public int hashCode()
     {
          return name.hashCode();
     }
     @Override
     public String toString()
     {
          return "Database connection with " + name;
     }
}
Listing 6-22

The DatabasePrincipal.java source code file

The DatabaseLoginModule.java and DatabasePrincipal.java source code is part of the example IBM FileNet Component Integration Workflow Operations code.

Adding the com.ibm.filenet.ps.ciops.test Package

A screenshot of a new java package window. It displays the options to create a new java package, where the name is given as com dot i b m dot file net dot p s dot c i o p s dot test.

Figure 6-27

The com.ibm.filenet.ps.ciops.test package is added

The com.ibm.filenet.ps.ciops.test package is part of the example IBM FileNet Component Integration Workflow Operations code.

Adding the Configuration Java Class

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, configuration, and the finish button is selected.

Figure 6-28

The Configuration class code is added to the com.ibm.filenet.ps.ciops.test package

The Java code for the Configuration.java source is as follows.
package com.ibm.filenet.ps.ciops.test;
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions.  IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you “AS IS “
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
import java.util.MissingResourceException;
import java.util.ResourceBundle;
public class Configuration {
     private static final String BUNDLE_NAME = "com.ibm.filenet.ps.ciops.test.configuration"; //$NON-NLS-1$
     private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
     private Configuration() {
     }
     public static String getParameter(String key) {
          try {
               return RESOURCE_BUNDLE.getString(key);
          } catch (MissingResourceException e) {
               return '!' + key + '!';
          }
     }
}
Listing 6-23

The Configuration.java source code file

Note

The preceding code uses a Java utility ResourceBundle. Resource bundles contain locale-specific objects. When a program needs a locale-specific resource, such as a String, the program can load it from the resource bundle that is for the current user's locale.

Adding the QOperationsTest Java Class

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, Q operation test, and the finish button is selected.

Figure 6-29

The QOperationsTest class code is added to the com.ibm.filenet.ps.ciops.test package

The Java code for the QOperationsTest.java source is as follows.
package com.ibm.filenet.ps.ciops.test;
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions.  IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you “AS IS “
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
import static org.junit.Assert.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.security.auth.Subject;
import com.ibm.filenet.ps.ciops.QOperations;
import org.junit.BeforeClass;
import org.junit.Test;
import com.filenet.api.constants.PropertyNames;
import com.filenet.api.core.Connection;
import com.filenet.api.core.Document;
import com.filenet.api.core.Domain;
import com.filenet.api.core.EntireNetwork;
import com.filenet.api.core.Factory;
import com.filenet.api.core.Folder;
import com.filenet.api.core.ObjectStore;
import com.filenet.api.util.UserContext;
import filenet.vw.api.VWAttachment;
import filenet.vw.api.VWAttachmentType;
import filenet.vw.api.VWException;
import filenet.vw.api.VWLibraryType;
import filenet.vw.api.VWSession;
/**
 * This class contains code for off line testing of the custom component {@link com.ibm.filenet.ps.ciops.QOperations QOperations}. To
 * run this class the different configuration parameters must be set in the configuration.properties file.
 *
 * @author Ricardo Belfor
 *
 */
public class QOperationsTest
{
     private static final String EMAIL_TO = Configuration.getParameter("QOperations.EmailTo"); //$NON-NLS-1$
     private static final String EMAIL_FROM = Configuration.getParameter("QOperations.EmailFrom"); //$NON-NLS-1$
     private static final String EMAIL_TEMPLATES_PATH = Configuration.getParameter("QOperations.EmailTemplatePath"); //$NON-NLS-1$
     private static final String OBJECT_STORE_NAME = Configuration.getParameter("QOperations.ObjectstoreName"); //$NON-NLS-1$
     private static final String TEST_FOLDER_NAME = Configuration.getParameter("QOperations.TestFolderName"); //$NON-NLS-1$
     private static final String USERNAME = Configuration.getParameter("QOperations.Username"); //$NON-NLS-1$
     private static final String PASSWORD = Configuration.getParameter("QOperations.Password"); //$NON-NLS-1$
     private static final String CONNECTION_POINT_NAME = Configuration.getParameter("QOperations.ConnectionPointName"); //$NON-NLS-1$
     private static final String CE_WSI_URL = Configuration.getParameter("QOperations.CEWsiUrl"); //$NON-NLS-1$
     private static final String WASP_LOCATION = Configuration.getParameter("QOperations.WaspLocation"); //$NON-NLS-1$
     private static final String WSI_JAAS_CONFIG_FILE = Configuration.getParameter("QOperations.WsiJaasConfigFile"); //$NON-NLS-1$
     private static Connection connection;
     private static VWSession vwSession;
     /**
      * This method is run before testing is started. It creates connections to the Content Engine and
      * the Process Engine.
      *
      * @throws Exception
      */
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
          try {
               System.setProperty("java.security.auth.login.config", WSI_JAAS_CONFIG_FILE); //$NON-NLS-1$
               System.setProperty("wasp.location",WASP_LOCATION ); //$NON-NLS-1$
               String url = CE_WSI_URL;
               String connectionPointName = CONNECTION_POINT_NAME;
               String password = PASSWORD;
               String username = USERNAME;
               createCEConnection(username, password, url);
               createVWSession(username, password, url, connectionPointName);
          } catch (Exception e) {
               e.printStackTrace();
               throw e;
          }
     }
     private static void createCEConnection(String username, String password, String url) {
          connection = Factory.Connection.getConnection(url);
          Subject subject = UserContext.createSubject(connection, username, password, "FileNetP8"); //$NON-NLS-1$
          UserContext uc = UserContext.get();
          uc.pushSubject(subject);
     }
     private static void createVWSession(String username, String password, String url, String connectionPointName)
               throws VWException {
          vwSession = new VWSession();
          vwSession.setBootstrapCEURI(url);
          vwSession.logon( username, password, connectionPointName );
     }
     private QOperations getQOperations() {
          return new QOperations() {
               @Override
               protected Connection getConnection() {
                    return connection;
               }
               @Override
               protected VWSession getVWSession() throws VWException {
                    return vwSession;
               }
          };
     }
     /**
      * Test method for the {@link com.ibm.filenet.ps.ciops.QOperations.#getFolderDocuments(VWAttachment)} method. It
      * uses a test folder in the object store as input.
      *
      * @throws Exception
      */
     @Test
     public void testGetFolderDocuments() throws Exception {
          VWAttachment folderAttachment = getTestFolder();
          QOperations  QOperations  = getQOperations();
          VWAttachment[] folderDocuments = QOperations.getFolderDocuments(folderAttachment);
          showResults(folderDocuments);
     }
     /**
      * Test method for the {@link com.ibm.filenet.ps.ciops.QOperations.#sendJMSMessage(VWAttachment docAttachment, String Message ) sendJMSMessage()} method.
      *
      * @throws Exception
      */
     @Test
     private void showResults(VWAttachment[] folderDocuments) {
          System.out.println( folderDocuments.length  +  " documents found" ); //$NON-NLS-1$
          for (VWAttachment attachment : folderDocuments) {
               System.out.println( attachment.toString() );
          }
     }
     private VWAttachment getTestFolder() throws VWException {
          ObjectStore objectStore = getTestObjectStore();
          Folder folder = (Folder) objectStore.getObject( "Folder", TEST_FOLDER_NAME ); //$NON-NLS-1$
          VWAttachment folderAttachment = getFolderAsVWAttachment(folder);
          return folderAttachment;
     }
     private ObjectStore getTestObjectStore()
     {
          EntireNetwork entireNetwork = Factory.EntireNetwork.fetchInstance(connection, null);
          Domain domain = entireNetwork.get_LocalDomain();
          ObjectStore objectStore = Factory.ObjectStore.getInstance( domain, OBJECT_STORE_NAME );
          return objectStore;
     }
     /**
      * Test method for the {@link com.ibm.filenet.ps.ciops.QOperations.#sendMailMessage(VWAttachment, String, String, String, String[], String[]) sendMailMessage()} method.
      *
      * @throws Exception
      */
     @Test
     private VWAttachment getDocumentAsVWAttachment(Document document) throws VWException {
          VWAttachment folderAttachment = getCEAttachment(document.getObjectStore() );
          document.fetchProperties( new String[] { PropertyNames.ID, PropertyNames.NAME } );
          folderAttachment.setId( document.get_Id().toString() );
          folderAttachment.setAttachmentName( document.get_Name() );
          folderAttachment.setType( VWAttachmentType.ATTACHMENT_TYPE_FOLDER );
          return folderAttachment;
     }
     private VWAttachment getFolderAsVWAttachment(Folder folder) throws VWException {
          VWAttachment folderAttachment = getCEAttachment(folder.getObjectStore() );
          folder.fetchProperties( new String[] { PropertyNames.ID, PropertyNames.NAME } );
          folderAttachment.setId( folder.get_Id().toString() );
          folderAttachment.setAttachmentName( folder.get_Name() );
          folderAttachment.setType( VWAttachmentType.ATTACHMENT_TYPE_FOLDER );
          return folderAttachment;
     }
     private VWAttachment getCEAttachment(ObjectStore objectStore) throws VWException {
          VWAttachment ceAttachment = new VWAttachment();
          ceAttachment.setLibraryType( VWLibraryType.LIBRARY_TYPE_CONTENT_ENGINE );
          objectStore.fetchProperties( new String[] { PropertyNames.NAME } );
          ceAttachment.setLibraryName( objectStore.get_Name() );
          return ceAttachment;
     }
     /**
      * Utility function for time stamping the different things produced by the test code. This
      * way the result of different tests can be kept apart.
      *
      * @return a timestamp string.
      */
     private static String getTimestamp() {
          SimpleDateFormat timestampFormatter = new SimpleDateFormat("yyyyMMdd HHmmSSS"); //$NON-NLS-1$
          return timestampFormatter.format( new Date() );
     }
}
Listing 6-24

The QOperationsTest.java source code file

Adding the AUDITReportMain Java Class

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, audit report main, and the finish button is selected.

Figure 6-30

The AUDITReportMain class code is added to the com.ibm.filenet.ps.ciops.test package

The Java code for the AUDITReportMain.java source is as follows.

This is the main program class which is called for the production of the Audit Report pdf files.
package com.ibm.filenet.ps.ciops.test;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
import javax.security.auth.Subject;
import org.apache.commons.io.LineIterator;
import com.asb.ce.utils.AUDITReportConfig;
import com.asb.config.AUDITConfig;
import com.filenet.api.collection.DocumentSet;
import com.filenet.api.collection.IndependentObjectSet;
import com.filenet.api.constants.FilteredPropertyType;
import com.filenet.api.constants.PropertyNames;
import com.filenet.api.constants.RefreshMode;
import com.filenet.api.core.CmTask;
import com.filenet.api.core.Connection;
import com.filenet.api.core.Document;
import com.filenet.api.core.Domain;
import com.filenet.api.core.EntireNetwork;
import com.filenet.api.core.Factory;
import com.filenet.api.core.Folder;
import com.filenet.api.core.IndependentObject;
import com.filenet.api.core.ObjectStore;
import com.filenet.api.property.FilterElement;
import com.filenet.api.property.PropertyFilter;
import com.filenet.api.query.SearchSQL;
import com.filenet.api.query.SearchScope;
import com.filenet.api.util.UserContext;
import com.ibm.filenet.ps.ciops.QOperations;
import com.ibm.filenet.ps.ciops.database.DatabasePrincipal;
import filenet.vw.api.VWAttachment;
import filenet.vw.api.VWAttachmentType;
import filenet.vw.api.VWException;
import filenet.vw.api.VWLibraryType;
import filenet.vw.api.VWSession;
import filenet.vw.base.logging.Logger;
import java.io.File;
//Q PDF Creation Operations and supporting methods 29th July 2022
import java.io.FileOutputStream;
import java.util.Date;
import com.lowagie.text.Anchor;
import com.lowagie.text.BadElementException;
//import com.lowagie.text.BaseColor;
import com.lowagie.text.Chapter;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Image;
import com.lowagie.text.List;
import com.lowagie.text.ListItem;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;
import com.lowagie.text.Section;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.pdf.RandomAccessFileOrArray;
import com.lowagie.text.pdf.codec.TiffImage;
//
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Vector;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FileUtils;
//import org.apache.commons.lang.math.RandomUtils;  //ASB removed this import 26-08-2022
import com.asb.ce.utils.*;
//
/**
IBM grants you a non-exclusive copyright license to use all programming code
examples from which you can generate similar function tailored to your own
specific needs.
All sample code is provided by IBM for illustrative purposes only.
These examples have not been thoroughly tested under all conditions.  IBM,
therefore cannot guarantee or imply reliability, serviceability, or function of
these programs.
All Programs or code component contained herein are provided to you “AS IS “
without any warranties of any kind.
The implied warranties of non-infringement, merchantability and fitness for a
particular purpose are expressly disclaimed.
© Copyright IBM Corporation 2013, ALL RIGHTS RESERVED.
*/
/**
 * Alan S.Bluck  11th August 2022
 * Code for Testing and Developing AUD_Operations
 * Adapted from:
 * Custom Java component serving as an example for the article
 * "Developing Java components for the Component Integrator".
 *
 * @author Ricardo Belfor
 *
 */
public class AUDITReportMain {
     public static String [] TaskParams = new String[100]; //ASB Array of the Case task type parameters
     public static String [] TaskName = new String[100]; //ASB Array of the Case task types
     public static String [] QStart = new String[100];   //ASB Array of the start question numbers
     public static String [] QEnd = new String[100];     //ASB Array of the end question numbers
     public static final Logger logger = Logger.getLogger(AUDITReportMain.class.getName());
     //private static final String EMAIL_TO = Configuration.getParameter("QOperations.EmailTo"); //$NON-NLS-1$
     private static String EMAIL_TO ; //ASB May need to change this to a list of users in the config.xml
     private static String EMAIL_FROM; //$NON-NLS-1$
     private static String EMAIL_TEMPLATES_PATH ;
     private static  String OBJECT_STORE_NAME;
     private static String TEST_FOLDER_NAME;
     private static String USERNAME;
     private static  String PASSWORD;
     private static String CONNECTION_POINT_NAME;
     private static String CE_WSI_URL;
     private static String WASP_LOCATION;
     private static String WSI_JAAS_CONFIG_FILE;
// Properties file load
     private static final Properties QQidMap = new Properties();
     private static Connection connection;
     private static VWSession vwSession;
     private static QOperations  QOperations;
     private static AUDITReportConfig cemc;
     private static void init() throws Exception{
          String fullPath = System.getProperty("user.dir") + File.separator
                    + "config" + File.separator + "QQID.properties";
          cemc = new AUDITReportConfig(); //ASB add our config.xml file methods here!
          try {
               EMAIL_TEMPLATES_PATH = cemc.getSMTPEmailTemplatePath();
               EMAIL_TO = cemc.getSMTPUser();
               EMAIL_FROM = cemc.getSourceEmailAddress();
               OBJECT_STORE_NAME = cemc.getExportOSName();
               TEST_FOLDER_NAME = cemc.getTestFolderName();
               USERNAME = cemc.getExportCEUser();
               PASSWORD = cemc.getExportCEPassword();
               CONNECTION_POINT_NAME = cemc.getExportConnectionPointName();
               CE_WSI_URL = cemc.getExportCEUrl();
               WASP_LOCATION = cemc.getExportWaspLocation();
               WSI_JAAS_CONFIG_FILE = cemc.getExportCELoginConfig();
             String url = cemc.getExportCEUrl();
               //String url = CE_WSI_URL;
             String connectionPointName = cemc.getExportConnectionPointName();
               //String connectionPointName = ;
               String password = cemc.getExportCEPassword(); //ASB Let's get it encrypted for security from config.xml
               //String password = PASSWORD;
               String username = cemc.getExportCEUser();
               //String username = USERNAME;
               createCEConnection(username, password, url);
               createVWSession(username, password, url, connectionPointName); //CHECK
          } catch (Exception e) {
               e.printStackTrace();
               throw e;
          }
          //
          try {
               QQidMap.load(new FileInputStream(new File(fullPath)));
               System.setProperty("java.security.auth.login.config", WSI_JAAS_CONFIG_FILE); //$NON-NLS-1$
               System.setProperty("wasp.location",WASP_LOCATION ); //$NON-NLS-1$
               return;
          } catch (FileNotFoundException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
          } catch (IOException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
          }
          throw new RuntimeException(fullPath + " cannot load QQid map");
          //java.sql.Connection databaseConnection = getDatabaseConnection();
          //if ( databaseConnection != null ) {
          //     logger.debug( databaseConnection.toString() );
          //} else {
          //     logger.debug( "No database connection" );
          //}
          }
     public static void main(String[] args) throws Exception {
          //We Could use the method below if required!
         //  final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
         init();
           while(cemc.getSTOPState().equalsIgnoreCase("No")) {
           // TODO Auto-generated method stub
          //Test the operations class
        // Set up the Operations Class for testing
          QOperations  = getQOperations();
          String[] TaskId = new String[1000];
          String[] TaskNames = new String[1000];
          String[] CaseId = new String[1000];
          int numberOfTasks=0;
//getCompletedTaskInfo(TaskId,TaskNames,CaseId,numberOfTasks);  //ASB    22-08-2022
            //ASB ...  Dynamically update the next Loader Start
            // Need date in the format 20130923T125628Z
          String sDeltaMinutes = cemc.getDeltaMinutes();
         String sDeltaHours = cemc.getDeltaHours();
         String sSearchDate = cemc.getQStartSearchDate();
         Integer iDeltaHours = Integer.valueOf(sDeltaHours);
         Integer iDeltaMinutes = Integer.valueOf(sDeltaHours);
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
     SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd",Locale.ENGLISH);
    SimpleDateFormat sdfTime = new SimpleDateFormat("HHmmss",Locale.ENGLISH);
        Date newDateSearch = new Date();
    String QstartSearchDate ="";
    logger.info("Audit Report Version 3.0 Build 14-08-2022:12-41PM" );
        try {
               //String sDate = sSearchDate.substring(0, 8);
               //String sTime = sSearchDate.substring(9,15);
              //newDateSearch = formatter.parse(sDate + sTime);
              Calendar cal = Calendar.getInstance();
              cal.setTime(newDateSearch);
              cal.add(Calendar.MINUTE, iDeltaMinutes);
              cal.add(Calendar.HOUR, iDeltaHours);
              newDateSearch = cal.getTime();
             // Need date in the format 20130923T125628Z
            QstartSearchDate = sdf.format(newDateSearch)+ "T" + sdfTime.format(newDateSearch)+ "Z";
             } catch (Exception e) {
                  e.printStackTrace();
             }
           getCompletedTaskInfo(TaskId,TaskNames,CaseId,numberOfTasks,QstartSearchDate);
            //
           cemc.updateProcessDate(newDateSearch);
           logger.info("Next Batch Date Start from Document :" + newDateSearch);
           Integer sleepTime = cemc.getSleepTime();
           TimeUnit.MINUTES.sleep(sleepTime);
            init();
           } //End Batch Loop
        }
     private static QOperations getQOperations() {
          return new QOperations() {
               @Override
               protected Connection getConnection() {
                    return connection;
               }
               @Override
               protected VWSession getVWSession() throws VWException {
                    return vwSession;
               }
          };
     }
    private static void getCompletedTaskInfo(String[] TaskId,String[] TaskNames,String[] CaseId, int NumberOfTasks,String sSearchDate) {
          try
          {   // Get data from  all completed Tasks to generate the related pdf files for attachment to the cases
               // From CmAcmCaseTask
              //String sSearchDate = cemc.getQStartSearchDate();
              logger.info("Start Search Date : " + sSearchDate);
              SearchSQL sqlObject = new SearchSQL();
              String selectList = cemc.getQSelectList();
              sqlObject.setSelectList(selectList); //Coordinator is the main Case Folder Object
              Integer maxRecords = 1000 ;//Should only be small numbers at any one time
              sqlObject.setFromClauseInitialValue("CmAcmCaseTask", "f", true); //set true to include subclasses
            String QWhereClause = cemc.getQWhereClause() + " " +  sSearchDate.trim();
              sqlObject.setWhereClause(QWhereClause); //ASB 11-08-2022 //NB 5 is the Complete status
            String osName = cemc.getExportOSName();
              ObjectStore objectStore = QOperations.getObjectStore(osName);  //ASB TODO Change to load from properties
              SearchScope search = new SearchScope(objectStore);
              PropertyFilter myFilter = new PropertyFilter();
              int myFilterLevel = 2; //Changed from 1 to 2 -- For performance !
              myFilter.setMaxRecursion(myFilterLevel);
              myFilter.addIncludeType(new FilterElement(null, null, null, FilteredPropertyType.ANY, null));
              // Set the (Boolean) value for the continuable parameter. This indicates
              // whether to iterate requests for subsequent pages of result data when the end of the
              // first page of results is reached. If null or false, only a single page of results is
              // returned.
              Boolean continuable = new Boolean(true); //SET To allow more results to be fetched
              // Set the page size (Long) to use for a page of query result data. This value is passed
              // in the pageSize parameter. If null, this defaults to the value of
              // ServerCacheConfiguration.QueryPageDefaultSize.
              Integer myPageSize = new Integer(1000);
            String SQL = sqlObject.toString();
              // Execute the fetchObjects method using the specified parameters.
              //ASB added set myFilter to null REF Best Practice API on FNRCA0024E: API_PROPERTY_NOT_IN_CACHE
              IndependentObjectSet myObjects = search.fetchObjects(sqlObject, myPageSize, null, continuable); //ASB myFilter  changed to null
//================================S T A R ================================T
              // You can then iterate through the collection of rows to access the properties.
             String [][] sTaskInfo = new String[1000][3]; // Index 0=f.Id , 1=f.CmAcmTaskName , 2= f.Coordinator Case Folder Id
              int rowCount = 1;
              Iterator iter = myObjects.iterator();
               while (iter.hasNext()){   //Get all Completed Tasks
                    NumberOfTasks = rowCount;
                    try
                    {
                        //Folder f = null;
                       IndependentObject object = (IndependentObject) iter.next();
                       //com.filenet.api.property.Properties props = object.getProperties();
                       //Iterator iterProps = props.iterator();
//============================ END =======================================
                    // Call Routine to retrieve the Question arrays
                      CmTask currentTask = (CmTask) object;
                      //String parentTaskClass = "CmTask"; //ASB 11-09-2017
                      //Update the Task Completed,AUD_FullQDateCompleted
                      // Get the properties collection
                      //com.filenet.api.property.Properties props =  currentTask.getProperties();
                      // Get the start date
                      //Date start = new Date(System.currentTimeMillis());
                      // Set the properties
                      //props.putValue("AUD_FullQDateCompleted", start);
                      //currentTask.save(RefreshMode.NO_REFRESH);
                      TaskNames[rowCount-1] = currentTask.getClassName();
                    TaskId[rowCount-1] =  currentTask.get_Id().toString();
                    CaseId[rowCount-1] = currentTask.get_Coordinator().get_Id().toString();
                         //sTaskInfo[rowCount] = ;
                         //sTaskInfo[rowCount,2] = " ";
                      //ASB 06-08-2022 Get the Current Case Folder Task Property definitions
                       //com.filenet.api.admin.ClassDefinition tClassDefs = Factory.ClassDefinition.fetchInstance(objectStore,taskClass, null); //update myFilter to null!!! ASB 06/08/2022
                       //PropertyDefinitionList tPropList = tClassDefs.get_PropertyDefinitions();
                      //ASB Get the Current Task Object properties
                      // 4. Populate the Case level Task Folder properties.
                      //getTaskProps(currentTask, taskClass,tClassDefs,tPropList,props,iterProps,sTableCells, questionValue, publishYes, questionDetails,nQuestions +1,nQuestionColumns);
//================================= E N D ================================
                    }catch(Exception errprop){
                   }
                    rowCount ++;
               }
//=========================================================================
               }catch(Exception e) {
                      System.out.println(e.getMessage()) ;
               }
               //ASB Check if we have no tasks to process currently!
               if(NumberOfTasks == 0) {
                    return;
               }
          //Generate the pdf files
          //
          String sTaskName ="";
        for (int index = 0; index < NumberOfTasks  ; index++) //ASB 15/08/2022 Changed from <= to < to fix bug!
        {
          String CaseGUID  = CaseId[index]; //Actually the Task Id whose properties are to be loaded
        String TaskGUID  = TaskId[index]; //{657A203A-9312-4D95-AC12-03292DB708F8}
        sTaskName = TaskNames[index]; // [AUD_ProduceFullQ]
          // pdfFileName =
          String pdfFileName =sTaskName + ".txt";
          // WorkingDirectory =
          String WorkingDirectory = cemc.getReportDirectoryName();
          //String CaseGUID, String TaskGUID, String TaskName, String pdfFileName, String WorkingDirectory
          String nQuestions = cemc.getNoQQuestions().toString();
          String nQuestionColumns =cemc.getNoQColumns().toString();
          //AUD_ProduceFullQ
          Integer paramCount = 0;
          Integer fieldCount = 0; //set for loading Fields
          String fieldVal="";
          //ASB  Retrieve the Task Name parameters from the config.xml
          for (String field:TaskName){
               //ASB Returns NotUsed string for any elements not in the config.xml file
               fieldVal = AUDITConfig.getConfigStringValue("config.QSECTIONS.S" + String.valueOf(fieldCount), "NotUsed");
                if (!fieldVal.equalsIgnoreCase("NotUsed")){
                     TaskParams = fieldVal.split(",");
                     TaskName[paramCount] = TaskParams[0];
                    QStart[paramCount] = TaskParams[1];
                    QEnd[paramCount] = TaskParams[2];
                    paramCount++;
                }
               fieldCount++;
          }
          fieldCount = 0;
        // Check the property we have
       try {
                for (String field:TaskName){
                     //ASB Returns NotUsed string for any elements
                     // not in the config.xml file
                  fieldVal = TaskName[fieldCount];
                  if(!(TaskName[fieldCount]==null)){
                      if(!fieldVal.equalsIgnoreCase("NotUsed")){
                        if(!(sTaskName==null)){ //ASB  Check for Null Task Name here
                           if(sTaskName.equalsIgnoreCase(TaskName[fieldCount])) {
                          // Process the Section
                           String StartQNo = QStart[fieldCount];
                           String EndQNo = QEnd[fieldCount];
                           String FileCreated = QOperations.generatePDF(CaseGUID, TaskGUID, sTaskName, pdfFileName, WorkingDirectory,nQuestions, nQuestionColumns,StartQNo, EndQNo);
                           QOperations.writePDFToCaseFolder(CaseGUID, TaskGUID, sTaskName,FileCreated, WorkingDirectory,nQuestions, nQuestionColumns,StartQNo, EndQNo);
                           //ASB Now Delete the pdf from the local working folder 15-08-2022
                              File fPDFFile = new File(FileCreated);
                                if(fPDFFile.exists()){
                                     fPDFFile.delete();
                                }
                           }
                        }else {
                             logger.error("Warning: No Task listed in config for the following Task retrieved :" + TaskName[fieldCount]);
                        }
                    }
               }
           fieldCount++;
           }
         } catch (Exception e) {
              logger.error(e.getMessage(), e);
         }
       }
          //return status;
    }
     private static void createCEConnection(String username, String password, String url) {
          connection = Factory.Connection.getConnection(url);
          Subject subject = UserContext.createSubject(connection, username, password, "FileNetP8"); //$NON-NLS-1$
          UserContext uc = UserContext.get();
          uc.pushSubject(subject);
     }
     private static void createVWSession(String username, String password, String url, String connectionPointName)
               throws VWException {
          vwSession = new VWSession();
          vwSession.setBootstrapCEURI(url);
          vwSession.logon( username, password, connectionPointName );
     }
}
Listing 6-25

The AUDITReportMain.java source code file

Adding the Audit_Report Project Classpath Properties

A cropped screenshot of a page displays a list under the audit report, where the properties option is selected.

Figure 6-31

The Audit_Report project properties classpath edits

As can be seen in Figure 6-31, there are flagged issues (with a cross on a red background icon) which we need to resolve using edits to the Audit_Report project properties, to add reference jar files for missing Java .jar class imports.

A screenshot of a window labeled properties for the audit report. It displays a list, where the java builds path option under the resource in libraries tab, and add external JARs tab are highlighted.

Figure 6-32

The Add External JARs command button is selected

We can add the required IBM FileNet and iText .jar files to the Eclipse classpath using the Add External JARs… command button in the Libraries tab for the Java Build Path menu item.

A screenshot of a JAR selection window. It displays a set of files under the file net jars, where all are selected.

Figure 6-33

The /opt/FileNetJars API .jar files are selected

The jar file list on Linux is as follows:
  348 -rw-r--r--.  1 root root     353793 Jan 22  2020 commons-codec-1.15.jar
   44 -rwxrwxrwx.  1 root root      42944 Jun  6 16:24 eeapi.jar
 3244 -rwxrwxrwx.  1 root root    3317773 Jun  6 16:24 Jace.jar
  480 -rwxrwxrwx.  1 root root     489883 Jun  6 16:25 log4j-1.2.17.jar
  480 -rwxrwxrwx.  1 root root     489883 Jun  9 08:55 log4j.jar
 3764 -rwxrwxrwx.  1 root root    3850486 Jun  6 16:27 pe3pt.jar
10192 -rwxrwxrwx.  1 root root   10433010 Jun  6 16:27 pe.jar
 7344 -rwxrwxrwx.  1 root root    7517605 Jun  6 16:28 peResources.jar
 1772 -rw-r--r--.  1 root root    1812019 Nov 15  2001 xerces.jar
(base) [root@ECMUKDEMO6 FileNetJars]#

A screenshot of properties for the audit report window. It displays a set of files under the libraries tab of the java build path in the resource. The commons codec 1.15 dot jar drop-down box is selected. Apply and close button is highlighted.

Figure 6-34

The Apply and Close command button is clicked

Adding the iText 2.1.7 Jar File for the PDF Creation Calls

Next, we copy the iText version 2.1.7 jar file, modified from the project source code (see later) to correct some issues.

A screenshot of a dark screen labeled root at E C M U K DEMO 6 colon slash opt slash i text jar. It displays a set of commands, where the file net jars, jars, and installs LINUX are highlighted.

Figure 6-35

The com.lowagie.text-2.1.7.jar is copied to /opt/iTextJar

We run the commands, as follows, to copy com.lowagie.text-2.1.7.jar onto our Linux RHEL 8.0 VMware system:
cd /opt
mkdir iTextJar
cd iTextJar
ls /mnt/hgfs
cp /mnt/hgfs/Installs/com.lowagie.text-2.1.7.jar .

A cropped screenshot of a JAR selection window. A file named com dot low a g i e dot text dash 2 dot 1 dot 7 dot jar is selected.

Figure 6-36

The com.lowagie.text-2.1.7.jar is selected

The com.lowagie.text-2.1.7.jar is added to the Audit_Report Eclipse project classpath.

A window labeled properties for the audit report. It has a set of files under the java build path, where the file com dot low a g I e dot text dash 2 dot 1 dot 7 dot jar is selected.

Figure 6-37

The com.lowagie.text-2.1.7.jar is added to the classpath

Adding the JUnit Java Test Library Jar File

A screenshot of properties for the audit report window. It displays a set of files under the libraries tab of the java build path in the resource drop-down option, where the add library tab is highlighted.

Figure 6-38

The Add Library command button is selected

In Figure 6-38, we click the Add Library… command to add the JUnit library.

A screenshot of the add library window. It displays a set of libraries, where the J unit library and next button are highlighted.

Figure 6-39

The JUnit library is added to the classpath

We must select the correct JUnit version from the drop-down options as shown in Figure 6-40.

A screenshot of the add library window. It displays the options under the J unit library, where the J unit 4 version and finish button are highlighted.

Figure 6-40

The JUnit 4 version is selected from the drop-down

We show the JUnit 4 library (used for Java assertion testing in our project).

A screenshot of properties for the audit report window. It displays the files under the libraries tab of the java build path, where the J unit 4 library and apply and close buttons are highlighted.

Figure 6-41

The JUnit 4 library is added and saved using Apply and Close

Adding the Apache Commons IO Jar File

Next, we need to download the latest Apache Commons IO jar file version 2.11.0 for the supporting I/O file commands we are using with Java 8.

A screenshot of an Apache commons page. It displays the welcome page with the options for downloading the apache commons I O 2 dot 11 dot 0 version of the application.

Figure 6-42

The commons-io-2.11.0-bin.tar.gz file is downloaded

We can now save the downloaded commons-io-2.11.0-bin.tar.gz file to the /root/Downloads folder path on Linux.

A screenshot of the opening commons dash i o dash 2 dot 11 dot 0 dash bin dot t a r dot g z window. It displays the downloaded file, and the save file and ok buttons are highlighted.

Figure 6-43

The commons-io-2.11.0-bin.tar.gz file download is saved

The downloaded commons-io-2.11.0-bin.tar.gz is copied to the /opt/apache_commons jar file folder and unpacked.

A screenshot of a dark screen. It displays a set of commands, where the command commons dash i o dash 2 dot 11 dot 0 dash bin dot t a r dot g z is highlighted.

Figure 6-44

The commands to unpack commons-io-2.11.0-bin.tar.gz

The following Linux commands were used to unpack the commons-io-2.11.0-bin.tar.gz file:
cd /opt
mkdir apache_commons
cd apache_commons
cp /root/Downloads/commons-io-2.11.0-bin.tar.gz .
tar -zxvf commons-io-2.11.0-bin.tar.gz

A screenshot of properties for the audit report window. It displays the files under the libraries tab of the java build path, where the add external JARs tab is highlighted.

Figure 6-45

The Add External JARs… command is selected

Now we can add the missing Apache Commons 2.11.0 jar file.

A screenshot of a JAR selection window. It displays a list of files. A file named commons dash i o dash 2 dot 11 dot 0 dot jar is highlighted.

Figure 6-46

The commons-io-2.11.0.jar file is added to the classpath

A screenshot of properties for the audit report window. It has the files under the java build path in the resource drop-down option, where the commons dash i o dash 2 dot 11 dot 0 dot jar and apply and close options are highlighted.

Figure 6-47

The commons-io-2.11.0.jar file is saved in the classpath

The commons-io-2.11.0.jar file, selected from the /opt/apache_commons/commons-i0-2.11.0 folder, is saved in the classpath using the Apply and Close command button shown in Figure 6-47.

Adding the Apache Commons Lang Java 8 Language Jar File

We also need the Apache Commons Lang jar file.

A cropped screenshot of the downloaded file. It reads Apache commons lang 3 dot 12 dot 0, Java 8 + in brackets. It lists the two files.

Figure 6-48

The Apache Commons Lang 312.0 jar file is downloaded

We click the link commons-lang3-3.12.0-bin.tar.gz for the Linux version.

A screenshot of a page has a set of commands. It reads the downloaded and installed files of apache commons.

Figure 6-49

The commons-lang3-3.12.0-bin.tar.gz file is unpacked

After downloading the commons-lang3-3.12.0-bin.tar.gz, we unpack it as follows:
cd /opt/apache_commons/
cp /mnt/hgfs/Installs/commons-lang3-3.12.0-bin.tar.gz .
tar -zxvf commons-lang3-3.12.0-bin.tar.gz

A screenshot of a JAR selection window. A file named commons dot lang 3 dash 3 dot 12 dor 0 dot jar, and the open buttons are highlighted.

Figure 6-50

The commons-lang3-3.12.0.jar file, selected for the classpath

We can now add the commons-lang3-3.12.0.jar file to the Eclipse Audit_Report project classpath.

A window of properties for the audit report. It displays the files under the java build path in the resource drop-down box, where the commons dot lang 3 dash 3 dot 12 dor 0 dot jar, and apply and close buttons are highlighted.

Figure 6-51

The commons-lang3-3.12.0.jar file is saved to the classpath

We use the Apply and Close command button to save the selected commons-lang3-3.12.0.jar file to the Audit_Report project classpath.

Adding the Config Subfolder to the Audit_Reports Project

A cropped screenshot of a page displays a list under the audit report, where new is selected, that further lists a menu, where the folder is selected.

Figure 6-52

The New Folder menu is selected

We need to create a config subfolder under the Audit_Report Eclipse project to hold the config.xml and log4j.xml.

A screenshot of a new folder window. It displays the options to create a new folder resource, where the audit report is selected, and the folder name is given as, config.

Figure 6-53

The config folder is created under the Audit_Report folder

A cropped screenshot of a page displays a list under the config, where new is selected, and further lists a menu, where the file is selected.

Figure 6-54

The File option is selected under the new config folder

Under the new config folder, we can now create the config.xml file as shown in Figure 6-55.

A screenshot of a create new file window. It displays the options to create a new file resource. Config is selected under the parent folder with the file name config dot x m l.

Figure 6-55

The config.xml file is created under the config folder

The new config.xml file is used to define the variables we use for driving the Audit Report program search for Audit tasks. This is as follows for the single Task we created in the IBM Case Manager, Audit Master solution.
<?xml version="1.0" encoding="UTF-8"?><config>
    <!-- AUDIT Question Configuration File for the PDF File Generation Program AUDITReport.jar -->
    <!-- Config for AUDIT Question Version 3.0 Build 14-08-2022:23:57PM  -->
    <maximum_docs>10000</maximum_docs>
    <!-- Setting the value below to Yes will stop the pdf generation process on the next run -->
    <!-- Otherwise it will loop ad-infinitum at SleepTime minute intervals -->
    <SET_TO_STOP>Yes</SET_TO_STOP>
      <!-- If SET_TO_STOP is No, then loop ad-infinitum at SleepTime minute intervals -->
    <SleepTime>2</SleepTime>
    <AUD_Select_List>f.Id,f.CmAcmTaskName,f.Coordinator</AUD_Select_List>
     <!-- Here search for Complete (Task State =5) and Q (CmAcmLaunchMode = 2) and AUDITOR CmAcmLaunchMode = 1) Tasks  -->
    <!-- <AUD_where_clause>TaskState = 5 and (CmAcmLaunchMode = 1 OR CmAcmLaunchMode = 2) AND f.DateLastModified &gt;  </AUD_where_clause> -->
     <!-- Here search for (CmAcmLaunchMode = 2) and AUDITOR CmAcmLaunchMode = 1 ) Tand CmAcmLaunchMode = 0 Tasks  -->
    <AUD_where_clause>AUD_Status='PDFCreation' and (CmAcmLaunchMode = 0 OR CmAcmLaunchMode = 1 OR CmAcmLaunchMode = 2) AND f.DateLastModified &gt;  </AUD_where_clause>
    <AUD_orderby_clause>d.DateLastModified,d.MajorVersionNumber, d.MinorVersionNumber</AUD_orderby_clause>
    <QCase_Folder_where_clause>CmAcmCaseState = 3 AND f.DateLastModified</QCase_Folder_where_clause>
    <QCase_Folder_orderby_clause>f.DateLastModified </QCase_Folder_orderby_clause>
    <AUDITOR_where_clause>d.DateLastModified</AUDITOR_where_clause>
    <AUDITOR_orderby_clause/>
    <ReportDirectoryName>/opt/AuditReport</ReportDirectoryName>
    <NumberOfFullQQuestions>16</NumberOfFullQQuestions>
    <NumberOfFullQColumns>3</NumberOfFullQColumns>
    <!-- Add Table Column Name, Width for each of 3 columns in the Section Tables - above defines the number of columns to use -->
    <PDFTable4>
                <C0>Change,5.0f</C0>
                <C1>Y/N,1.0f</C1>
                <C2>Details,3.2f</C2>
    </PDFTable4>
         <!-- Enter the symbolic name of the main AUDITOR Workflow Task -->
    <AUDITOR_Task>AUD_AuditProcess</AUDITOR_Task>
         <!-- Add Tab names as the Header, Number of Properties and Symbolic Case Property List additional sections can be added -->
           <!-- as T1 T2 T3 etc -->
    <TabSECTIONS_AUDITOR>
                <T0>1. General,2,AUD_DepartmentName,AUD_DepartmentNumber</T0>
                <T1>2. Question Section 1,6,AUD_General_1_Q1,AUD_General_1_Q2,AUD_General_1_Q3,AUD_General_1_Q4,AUD_General_1_Q5,AUD_General_1_Q6</T1>
                <T2>3. Question Section 2,10,AUD_General_1_Q7,AUD_General_1_Q8,AUD_General_1_Q9,AUD_General_1_Q10,AUD_General_1_Q11,AUD_General_1_Q12,AUD_General_1_Q13,AUD_General_1_Q14,AUD_General_1_Q15,AUD_General_1_Q16</T2>
    </TabSECTIONS_AUDITOR>
     /<TabSECTIONS_Audit>
     </TabSECTIONS_Audit>
    <TabSECTIONS_Department>
    </TabSECTIONS_Department>
    <!-- Section Question Tables- Add section names as the full Task name , start question number, end question number for the section -->
     <!-- Additional sections can be added as S2, S3 etc if more Audit properties or Tasks are added to the solution  -->
    <QSECTIONS>
                <S0>AUD_AuditProcess,1,8</S0>
                <S1>AUD_AuditProcess,8,16</S1>
    </QSECTIONS>
    <TestFolderName>/Templates</TestFolderName>
    <database>
        <SearchSubClasses>yes</SearchSubClasses>
        <!-- Note that this section can contain Document Classes or Case Folder Classes  -->
        <!-- Note that this section can contain ONLY Document Classes For the case manager documents  -->
         <docclass_mapping>
              <d0>AUD_AUDITOR</d0>
              <d1>AUD_AUDITOR</d1>
         <!-- Uncomment below to search additional Document classes as required
               <d2>Document</d2>
               <d10>NotUsed</d10>    -->
        </docclass_mapping>
        <!-- Note that this section can contain ONLY Folder Classes For the Case Management Folders  -->
         <folderclass_mapping>
               <d0>AUD_AuditReport</d0>
     <!-- Uncomment below to search additional Folder classes as required
               <d10>NotUsed</d10>    -->
        </folderclass_mapping>
    </database>
    <ceimport>
        <osname>OS1</osname>
        <osrootfolder>/AUDIT_TEST</osrootfolder>
        <ceuser>Alan</ceuser>
        <cepassword>RW5jcnlwdDNkZmlsXxXxXxXxXxXxXxXx</cepassword>
        <ceurl>http://ecmukdemo6:9080/wsi/FNCEWS40MTOM/</ceurl>
          <celoginconfig>/opt/IBM/FileNet/CEClient/config/jaas.conf.WSI</celoginconfig>
        <cestanza>FileNetP8WSI</cestanza>
        <UpdatingBatchSize>250</UpdatingBatchSize>
        <MaxDocCount>50</MaxDocCount>
        <MaxRunTimeMinutes>30</MaxRunTimeMinutes>
        <MaxConsecutiveErrors>1000</MaxConsecutiveErrors>
        <AuditFile>/opt/replication/logs/AuditImportAuditDocs.log</AuditFile>
        <AuditFileFolders>/opt/replication/logs/AuditImportAuditFolders.log</AuditFileFolders>
    </ceimport>
    <AUDITProcessor>
        <MaxSearchSize>5000</MaxSearchSize>
        <startsearchdate>20220824T133429Z</startsearchdate>
         <!--  search Date is calculated as System Date - Search Days  -->
        <searchdays>0</searchdays>
        <PropCardinalityList>
             <PropCardinality>2</PropCardinality>
             <PropCardinality>2</PropCardinality>
             <PropCardinality>0</PropCardinality>
        </PropCardinalityList>
        <DocumentPropertyNames>
            <PropertyName>ToEmailAddress</PropertyName>
            <PropertyName>FromEmailAddress</PropertyName>
            <PropertyName>FileName</PropertyName>
         </DocumentPropertyNames>
        <DocumentPropertyTypes>
             <PropertyType>8</PropertyType>
             <PropertyType>8</PropertyType>
             <PropertyType>8</PropertyType>
        </DocumentPropertyTypes>
        <DocumentWhiteListFlags>
             <PropFlag>NONE</PropFlag>
             <PropFlag>NONE</PropFlag>
             <PropFlag>NONE</PropFlag>
        </DocumentWhiteListFlags>
        <DebugOutputFlag>on</DebugOutputFlag>
        <searchProperty>Id</searchProperty>
        <searchPropertyDocuments>Id</searchPropertyDocuments>
        <searchPropertyFolders>Id</searchPropertyFolders>
         <DeltaHours>-96</DeltaHours>
         <DeltaMinutes>0</DeltaMinutes>
         <folderCaseClasses>AuditDepartment</folderCaseClasses>
         <JDBC>jdbc:oracle:thin:@ECMUKDEMO1:1521:orcl</JDBC>
         <JDBCDriverClass>oracle.jdbc.driver.OracleDriver</JDBCDriverClass>
         <JDBCUser>P8DB_ECMUK_04</JDBCUser>
         <JDBCPassword>RW5jcnlwdDNkZmlsXxXxXxXxXxXxXxXx</JDBCPassword>
         <EmailListName>FromEmailAddresses</EmailListName>
         <EmailFlagListName>EmailFlag</EmailFlagListName>
    </AUDITProcessor>
    <ceexport>
        <osname>OS2</osname>
        <ceuser>Alan</ceuser>
        <cepassword>RW5jcnlwdDNkZmlsXxXxXxXxXxXxXxXx</cepassword>
        <MaxDocCount>500</MaxDocCount>
        <MaxRunTimeMinutes>0</MaxRunTimeMinutes>
        <MaxConsecutiveErrors>1000</MaxConsecutiveErrors>
        <AuditFile>/opt/replication/logs/AuditExportAudit.log</AuditFile>
        <SQLBatchSize>250</SQLBatchSize>
        <celoginconfig>/opt/IBM/FileNet/CEClient/config/jaas.conf.WSI </celoginconfig>
        <ceurl>http://ecmukdemo6:9080/wsi/FNCEWS40MTOM</ceurl>
        <cestanza>FileNetP8WSI</cestanza>
        <MimeType>application/pdf</MimeType>
        <DocClassName>AUD_AuditReport</DocClassName>
        <CaseStatus>Initial</CaseStatus>
        <CaseStatusField>CaseStatus</CaseStatusField>
        <ConnectionPointName>CP2</ConnectionPointName>
        <WaspLocation/>
    </ceexport>
    <SMTPreport>
                <sourceEmailAddress>[email protected]</sourceEmailAddress>
                <sourceEmailPassword>RW5jcnlwdDNkR2xhXxXxXxXxXxXxXxXx</sourceEmailPassword>
                <SMTPTLSRequired>true</SMTPTLSRequired>
                <SMTPSSLRequired>false</SMTPSSLRequired>
                <SMTPHost>smtp-relay.gmail.com</SMTPHost>
                <SMTPUser>[email protected]</SMTPUser>
                <TLSPort>587</TLSPort>
                <SSLPort>465</SSLPort>
                <SMTPType>TLS</SMTPType>
                <EmailTemplatePath> </EmailTemplatePath>
     </SMTPreport>
</config>
Listing 6-26

The config.xml configuration file list for the Audit Report parameters

Adding the AuditTest Java Class

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as an audit test. The public static void main and finish buttons are highlighted.

Figure 6-56

The AuditTest class is created under the Audit Report project

The full code is shown under the “Part 1 – Bill of Materials” section of the chapter.

(“A Java Program to create a Simple Audit PDF document using the iText library.”)

A cropped screenshot of a page titled audit test dot java. It displays a set of program codes, that defines the package, public glass, and public static void.

Figure 6-57

The template code is created

A screenshot of a page titled debug. It displays a set of files, where the audit test dot main string is selected. It runs a program under the audit test dot java file.

Figure 6-58

The AuditTest Java code is entered and run

The output from this test code is shown in the AuditTest.pdf file, which was created as shown in Figure 6-59.

A screenshot of a window is overlayed by the audit report test P D F generation window. The tabs opt, audit report, audit test, and audit test dot p d f are highlighted.

Figure 6-59

The AuditTest.pdf output from the code in Figure 6-58

Adding the AuditMasterLogoTest Java Class

Next, we set up the AuditMasterLogoTest.java test code to embed a TIF image into the Audit Report pdf document. This code was listed under the “Example 2 – Test Code to Add the Audit Master Logo” section of the chapter. (A Java program for adding the Audit Master Logo image to the Audit report document.)

A screenshot of a new java class window. It displays the options to create a new java class, where the name is given as, audit master logo test.

Figure 6-60

The AuditMasterLogoTest Java Class is created

The template in Figure 6-61 is generated for the AuditMasterLogoTest.java.

A cropped screenshot of a screen reads a set of program codes under the audit master logo test dot java file. It reads the package, public class, and the public static void.

Figure 6-61

The template Java code is created for AuditMasterLogoTest

A cropped screenshot of a screen. It displays the java codes for audit reports, audit tests, m k d i r images, and installs the application.

Figure 6-62

The Audit Master Logo TIF file is copied to the images folder

Before running any code, we need to create and copy the Audit Master TIF image file, AuditMasterLogo.tif, to the /opt/AuditReport/images folder. This is created using an original JPEG photograph image which was then saved as a TIF image using CorelDRAW photo editing software. The file image was then transferred into the Linux VMware environment using the following command lines:
cd /opt
mkdir AuditReport
cd AuditReport
mkdir AuditTest
mkdir images
cd images
cp /mnt/hgfs/Installs/AuditMasterLogo.tif .

A screenshot of a page titled debug, It lists a set of files, where the audit master logo test main string line 43 is selected. It displays a set of program codes.

Figure 6-63

The AuditMasterLogoTest.java program is successfully run

After running the Java code shown in Figure 6-63, we can check the contents of the AuditImage.pdf output file.

A screenshot of a window is overlayed by the audit image dot p d f window. It displays a picture of a flower. The tabs opt, audit report, audit test, and audit test are highlighted.

Figure 6-64

The AuditImage.pdf output file with the embedded TIF image

For the main Audit Report program, we need to be able to flag the IBM Case Manager Case Tasks (we have just one, the AUD_AuditProcess task).

We can create a new property to identify if we wish to create an Audit Report pdf file for a Case by setting the property with a default String value of “PDFCreation” which we can then include in a Content Object Store search string in the config.xml file.

Adding the IBM Case Manager Audit Report Questions

We can now update the Audit Master solution using the IBM Case Manager Builder application to add the additional Case properties required by the Audit Report.

A screenshot of the I B M case manager builder page. It displays the audit master page. The status under name, description, default value and unique identifier are highlighted.

Figure 6-65

The Status for the solution is set by default to “PDFCreation

Additionally, we add 16 standard Audit Question properties to the AUD_AuditProcess task as listed at the beginning of this section (Example 2).

The question property names are structured so that they can be referenced in the config.xml as a generic stem with the question number defined in a specific place to assist with the Java code loop structure. This scheme provides greater flexibility and allows very efficient code to be produced.

A screenshot of a page displays the options under the properties tab. The details under name, description, default value and unique identifier are highlighted.

Figure 6-66

The generic questions are added for the Audit Report output

The questions are designed to be output with header section names as shown in the config.xml file.

A screenshot of a page displays the options under the properties tab. It displays a set of general files with the name, type, attributes, and description.

Figure 6-67

The 16 generic questions saved in the Audit Master solution

The 16 generic questions are added to the AUD_AuditProcess task properties section, as shown in Figure 6-68.

A screenshot of a page displays the options under the task properties tab. It displays a set of general files under the existing property. It lists the files with name, unique i d, and type.

Figure 6-68

The Task Properties tab of the Audit Process task is shown

A screenshot of a page. The audit status under the name and the P D F creation under default values are highlighted.

Figure 6-69

The Audit Status property is created with a default PDFCreation

The Audit Master solution changes are committed and the Solution redeployed. We then need to create new Audit Department cases to use for the Audit Report tests.

A screenshot of a page. It reads audit master with a darkly shaded circle and reads the letters A M inside it. The down arrow is highlighted and reads commit all of the components of the solution.

Figure 6-70

The Commit icon is clicked for the Audit Master solution

An IBM Case Manager Builder application pop-up window lists the changes to be saved.

A screenshot of a message box. It reads the message whether the user has locked the solutions and tasks. The commit my changes button is highlighted.

Figure 6-71

The Commit My Changes command button is selected

The yellow triangle icon with the exclamation mark indicates undeployed edits, so, next, we select the anti-clockwise arrow icon to redeploy the Audit Master solution to include the additional Task properties which will be used to create the Audit Report pdf file.

A screenshot of a page. It reads audit master with a shaded circle with the letters A M inside it. The up arrow is highlighted and reads deploy the changed solution artifacts into a set environment.

Figure 6-72

The Audit Master solution is redeployed

The successful Audit Master Solution deployment is indicated by a white tick icon with a green background (a failed deployment would be indicated by a white cross on a red background).

A screenshot of a page. It reads audit master with a shaded circle with the letters A M inside. It displays the last modified status. The upward arrow is selected.

Figure 6-73

The Audit Master Solution status is indicated with a tick mark

Testing the Audit Report Program

A new Audit Department Case is created with the additional questions.

A screenshot of an I B M case manager page. It lists a set of cases under work. Case A U D audit department 000000140001 along with its modified date are highlighted.

Figure 6-74

The new Audit Department Case is highlighted

The Audit Report program is driven by IBM Case Manager Tasks. The config.xml file is designed to support a Main Case Task, followed by zero or as many additional Case Manager Task types as required.

A screenshot of an I B M case manager Mozilla firefox page. It displays the case details with business objectives. The start button is highlighted under the audit process.

Figure 6-75

The optional Audit Process Task Start command is selected

The Task start is confirmed by selecting the Yes command from a pop-up confirmation window.

A screenshot of a confirmation message box. It reads the question to start the task. The yes button is highlighted.

Figure 6-76

The Audit Process Task Start command is confirmed

A screenshot of an I B M case manager page. It displays the case details of A U D audit department 000000140001. The history and the task completed under the audit process are highlighted.

Figure 6-77

The History tab of the opened Audit Department Case

The Audit Department Case shows that the Audit Process task has successfully completed.

Before we run the Audit Report program, we have to create a subfolder under the /opt/AuditReport directory with the name of the Case Task, since each IBM Case Manager Task is designed to generate a separate Audit Report pdf file. Also, the individual case reports are identified and separated from each other because the program uses the unique Task Id of the stored Case as the first part of the Audit report filename.

A screenshot of a page. It reads a set of program codes for the audit report, audit process, and audit test.

Figure 6-78

The AuditReport task subfolder AUD_AuditProcess is created

The following command-line code is used to create the Audit Report task subfolder:
cd /opt/AuditReport
mkdir AUD_AuditProcess

After the Audit Report pdf is created, the file is uploaded back to the Audit Department Case Folder and then deleted from the Linux folder (which is used just as a temporary storage area).

A screenshot of a page labeled e c m u k demo 6. It displays the files under the O S 2, where 000000140001 is selected. It reads the content, where the content name is highlighted.

Figure 6-79

The highlighted Audit Process task pdf is stored with the Case

This pdf file is stored in the OS2 IBM Case Manager target Object Store as an Audit Report class document.

A screenshot of a page displays the content under the general tab of C 0 D C E 382. The document title is highlighted.

Figure 6-80

The Audit Process pdf file name shows the Task Id (highlighted)

A screenshot of a page. It has a set of program codes for the audit process.

Figure 6-81

The (temporary) Audit Report file is displayed in the directory

We have paused the program in debug mode just before running the delete statement, as shown in the code screenshot in Figure 6-82.

A screenshot of a page. It has a set of program codes to delete the p d f from the local working folder and retrieves the output.

Figure 6-82

The Java file delete statement will remove the file in Figure 6-81

Creating the log4j.xml for the Audit Report Logs

The Log4J logs subdirectory is created using the following code:
cd /opt/AuditReport
mkdir logs

A screenshot of a page. It reads a set of program codes for the audit report of the logs.

Figure 6-83

The Log4J logs subdirectory is created

The log4j.xml file is created under the Audit_Report/src project folder.

A screenshot of a create new file window. The audit report s r c under the parent folder and there file name of log 4 j dot x m l, and the finish button are highlighted.

Figure 6-84

A log4j.xml file is created under an Audit_Report/src folder

The log4j.xml for the AuditReport Java program is configured as follows.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration>
     <appender name="rfa2" class="org.apache.log4j.RollingFileAppender">
          <param name="file" value="/opt/AuditReport/logs/AuditProcessor.log"></param>
          <param name="MaxFileSize" value="500KB"></param>
          <param name="MaxBackupIndex" value="100"></param>
          <layout class="org.apache.log4j.PatternLayout">
               <param name="ConversionPattern" value="%m%n"></param></layout>
          <filter class="org.apache.log4j.varia.LevelRangeFilter"><param name="levelMin" value="DEBUG"></param>
               <param name="levelMax" value="FATAL"></param></filter>
          </appender>
    <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
          <layout class="org.apache.log4j.PatternLayout">
               <param name="ConversionPattern" value="%m%n" />
          </layout>
          <filter class="org.apache.log4j.varia.LevelRangeFilter">
             <param name="levelMin" value="DEBUG" />
            <param name="levelMax" value="INFO" />
        </filter>
     </appender>
     <appender name="rfa" class="org.apache.log4j.RollingFileAppender">
         <param name="file" value="/opt/filenet_app.log"/>
         <param name="MaxFileSize" value="100KB"/>
         <!-- Keep some backup files -->
         <param name="MaxBackupIndex" value="10"/>
         <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" value="%p %t %c - %m%n"/>
         </layout>
         <filter class="org.apache.log4j.varia.LevelRangeFilter">
             <param name="levelMin" value="DEBUG" />
            <param name="levelMax" value="FATAL" />
        </filter>
       </appender>
       <appender name="rfa_threads" class="org.apache.log4j.RollingFileAppender">
         <param name="file" value="/opt/AuditReport/logs/filenet_JVM.log"/>
         <param name="MaxFileSize" value="500KB"/>
         <!-- Keep one backup file -->
         <param name="MaxBackupIndex" value="100"/>
         <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" value="%m%n"/>
                <!--<param name="ConversionPattern" value="%p %t %c - %m%n"/>-->
         </layout>
         <filter class="org.apache.log4j.varia.LevelRangeFilter">
             <param name="levelMin" value="INFO" />
            <param name="levelMax" value="INFO" />
        </filter>
       </appender>
     <logger name="com.ibm.filenet.ce" additivity="false">
          <level value="debug" />
          <appender-ref ref="rfa" />
          <appender-ref ref="stdout" />
     </logger>
     <logger name="filenet_error.api.com.filenet.apiimpl.util.ConfigValueLookup" additivity="false">
          <level value="debug" />
          <appender-ref ref="rfa" />
          <appender-ref ref="stdout" />
     </logger>
     <logger name="filenet_tracing.api.detail.moderate.summary.com.filenet.apiimpl.util.ConfigValueLookup" additivity="false">
          <level value="debug" />
          <appender-ref ref="rfa" />
          <appender-ref ref="stdout" />
     </logger>
     <logger name="com.asb.ce.utils.AUDITProcessorConfig" additivity="false">
        <level value="debug" />
          <appender-ref ref="rfa_threads" />
    </logger>
    <logger name="com.ibm.filenet.ps.ciops.test.AUDITReportMain" additivity="false">
          <level value="info"></level>
          <appender-ref ref="rfa2"></appender-ref>
          <appender-ref ref="stdout"></appender-ref>
    </logger>
     <root>
          <priority value="error" />
          <appender-ref ref="stdout" />
          <appender-ref ref="rfa" />
          <appender-ref ref="rfa2"></appender-ref></root>
     </log4j:configuration>
Listing 6-27

The log4j.xml list for the Audit Report error logging

The log4j.xml file also requires a log4j.dtd file in the same folder path, which defines the XML file tags used. This log4j.dtd contains the following lines.
<?xml version="1.0" encoding="UTF-8" ?>
<!--
 Licensed to the Apache Software Foundation (ASF) under one or more
 contributor license agreements.  See the NOTICE file distributed with
 this work for additional information regarding copyright ownership.
 The ASF licenses this file to You under the Apache License, Version 2.0
 (the "License"); you may not use this file except in compliance with
 the License.  You may obtain a copy of the License at
      http://www.apache.org/licenses/LICENSE-2.0
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-->
<!-- Authors: Chris Taylor, Ceki Gulcu. -->
<!-- Version: 1.2 -->
<!-- A configuration element consists of optional renderer
elements,appender elements, categories and an optional root
element. -->
<!ELEMENT log4j:configuration (renderer*, appender*,plugin*, (category|logger)*,root?,
                               (categoryFactory|loggerFactory)?)>
<!-- The "threshold" attribute takes a level value below which -->
<!-- all logging statements are disabled. -->
<!-- Setting the "debug" enable the printing of internal log4j logging   -->
<!-- statements.                                                         -->
<!-- By default, debug attribute is "null", meaning that we not do touch -->
<!-- internal log4j logging settings. The "null" value for the threshold -->
<!-- attribute can be misleading. The threshold field of a repository      -->
<!-- cannot be set to null. The "null" value for the threshold attribute -->
<!-- simply means don't touch the threshold field, the threshold field   -->
<!-- keeps its old value.                                                -->
<!ATTLIST log4j:configuration
  xmlns:log4j              CDATA #FIXED "http://jakarta.apache.org/log4j/"
  threshold                (all|trace|debug|info|warn|error|fatal|off|null) "null"
  debug                    (true|false|null)  "null"
  reset                    (true|false) "false"
>
<!-- renderer elements allow the user to customize the conversion of  -->
<!-- message objects to String.                                       -->
<!ELEMENT renderer EMPTY>
<!ATTLIST renderer
  renderedClass  CDATA #REQUIRED
  renderingClass CDATA #REQUIRED
>
<!-- Appenders must have a name and a class. -->
<!-- Appenders may contain an error handler, a layout, optional parameters -->
<!-- and filters. They may also reference (or include) other appenders. -->
<!ELEMENT appender (errorHandler?, param*,
      rollingPolicy?, triggeringPolicy?, connectionSource?,
      layout?, filter*, appender-ref*)>
<!ATTLIST appender
  name           CDATA      #REQUIRED
  class      CDATA     #REQUIRED
>
<!ELEMENT layout (param*)>
<!ATTLIST layout
  class          CDATA     #REQUIRED
>
<!ELEMENT filter (param*)>
<!ATTLIST filter
  class          CDATA     #REQUIRED
>
<!-- ErrorHandlers can be of any class. They can admit any number of -->
<!-- parameters. -->
<!ELEMENT errorHandler (param*, root-ref?, logger-ref*,  appender-ref?)>
<!ATTLIST errorHandler
   class        CDATA   #REQUIRED
>
<!ELEMENT root-ref EMPTY>
<!ELEMENT logger-ref EMPTY>
<!ATTLIST logger-ref
  ref CDATA #REQUIRED
>
<!ELEMENT param EMPTY>
<!ATTLIST param
  name          CDATA   #REQUIRED
  value          CDATA     #REQUIRED
>
<!-- The priority class is org.apache.log4j.Level by default -->
<!ELEMENT priority (param*)>
<!ATTLIST priority
  class   CDATA     #IMPLIED
  value       CDATA #REQUIRED
>
<!-- The level class is org.apache.log4j.Level by default -->
<!ELEMENT level (param*)>
<!ATTLIST level
  class   CDATA     #IMPLIED
  value       CDATA #REQUIRED
>
<!-- If no level element is specified, then the configurator MUST not -->
<!-- touch the level of the named category. -->
<!ELEMENT category (param*,(priority|level)?,appender-ref*)>
<!ATTLIST category
  class         CDATA   #IMPLIED
  name          CDATA     #REQUIRED
  additivity     (true|false) "true"
>
<!-- If no level element is specified, then the configurator MUST not -->
<!-- touch the level of the named logger. -->
<!ELEMENT logger (level?,appender-ref*)>
<!ATTLIST logger
  name          CDATA     #REQUIRED
  additivity     (true|false) "true"
>
<!ELEMENT categoryFactory (param*)>
<!ATTLIST categoryFactory
   class        CDATA #REQUIRED>
<!ELEMENT loggerFactory (param*)>
<!ATTLIST loggerFactory
   class        CDATA #REQUIRED>
<!ELEMENT appender-ref EMPTY>
<!ATTLIST appender-ref
  ref CDATA #REQUIRED
>
<!-- plugins must have a name and class and can have optional parameters -->
<!ELEMENT plugin (param*, connectionSource?)>
<!ATTLIST plugin
  name           CDATA         #REQUIRED
  class      CDATA  #REQUIRED
>
<!ELEMENT connectionSource (dataSource?, param*)>
<!ATTLIST connectionSource
  class        CDATA  #REQUIRED
>
<!ELEMENT dataSource (param*)>
<!ATTLIST dataSource
  class        CDATA  #REQUIRED
>
<!ELEMENT triggeringPolicy ((param|filter)*)>
<!ATTLIST triggeringPolicy
  name           CDATA  #IMPLIED
  class      CDATA  #REQUIRED
>
<!ELEMENT rollingPolicy (param*)>
<!ATTLIST rollingPolicy
  name           CDATA  #IMPLIED
  class      CDATA  #REQUIRED
>
<!-- If no priority element is specified, then the configurator MUST not -->
<!-- touch the priority of root. -->
<!-- The root category always exists and cannot be subclassed. -->
<!ELEMENT root (param*, (priority|level)?, appender-ref*)>
<!-- ================================================================== -->
<!--                       A logging event                              -->
<!-- ================================================================== -->
<!ELEMENT log4j:eventSet (log4j:event*)>
<!ATTLIST log4j:eventSet
  xmlns:log4j             CDATA #FIXED "http://jakarta.apache.org/log4j/"
  version                (1.1|1.2) "1.2"
  includesLocationInfo   (true|false) "true"
>
<!ELEMENT log4j:event (log4j:message, log4j:NDC?, log4j:throwable?,
                       log4j:locationInfo?, log4j:properties?) >
<!-- The timestamp format is application dependent. -->
<!ATTLIST log4j:event
    logger     CDATA #REQUIRED
    level      CDATA #REQUIRED
    thread     CDATA #REQUIRED
    timestamp  CDATA #REQUIRED
    time       CDATA #IMPLIED
>
<!ELEMENT log4j:message (#PCDATA)>
<!ELEMENT log4j:NDC (#PCDATA)>
<!ELEMENT log4j:throwable (#PCDATA)>
<!ELEMENT log4j:locationInfo EMPTY>
<!ATTLIST log4j:locationInfo
  class  CDATA     #REQUIRED
  method CDATA     #REQUIRED
  file   CDATA     #REQUIRED
  line   CDATA     #REQUIRED
>
<!ELEMENT log4j:properties (log4j:data*)>
<!ELEMENT log4j:data EMPTY>
<!ATTLIST log4j:data
  name   CDATA     #REQUIRED
  value  CDATA     #REQUIRED
>
Listing 6-28

The log4j.dtd file for the log4j.xml definitions

Verifying the Audit Report pdf File Structure

The output Audit Report pdf file, AUD_AuditProcess.pdf, can be viewed in the IBM FileNet Content Engine Administration Console (acce) web application Document Viewer.

A screenshot of a message box for opening. It reads the audit process files to be opened with the document viewer.

Figure 6-85

The Audit Report pdf, {3055E682-0000-C698-9C1A-091208504466}AUD_AuditProcess.pdf, is viewed

The IBM FileNet Document Viewer shows the Audit Report pdf thumbnails and the Header page.

A screenshot of the activities document viewer window. It displays the 2 pages of the A U D audit process dot t x t file.

Figure 6-86

The Logo Image (Page 1) and Header (Page 2) are visible

The Audit Report pdf document properties can be seen which, after clicking the Properties menu item, show the Header attributes we set in the Audit Report Java program.

A screenshot of a dropdown list from an extension. The properties option is highlighted.

Figure 6-87

The Properties… menu item is selected from the drop-down

A screenshot of the activities document viewer window is overlayed by the properties window. It displays the content of the A U D audit process dot t x t file under the general tab.

Figure 6-88

The generated Audit Report pdf Header attributes are shown

A screenshot of a page displays the content under the general tab of file 3055 E 682. It displays a dropdown list under the actions, where the view download option is highlighted.

Figure 6-89

The View/Download menu option we have used for the pdf

Creating the Audit Master Solution Workflow Audit Review Step

To provide the necessary review of the Audit Report Questions, a review step is created in the Workflow Designer of the IBM Case Manager for the Auditor role (see Chapter 1 for the full Audit Master solution build).

A step designer window. It displays the role lane, workgroup lane, step, stub step, rule step, property step, and stage step under the palette. It reads a map from the launch step to the audit review.

Figure 6-90

The Audit Review step is created for the Auditor role

A screenshot of the activities document viewer window. It displays page 3 of the A U D audit process dot t x t file. It reads the content under general, and question sections 1 and 2.

Figure 6-91

Page 3 of the Audit Report shows the Questions 1 to 10

A screenshot of the activities document viewer window. It displays page 4 of the A U D audit process dot t x t file. It reads the content under general, and questions 11 to 16.

Figure 6-92

Page 4 of the Audit Report shows the Questions 11 to 16

A screenshot of an I B M case manager page. It lists a set of files under the auditor of the work tab. The audit review is highlighted for the case identifier A U D audit department 000000150002.

Figure 6-93

The Audit Review Auditor Work Step we created in Figure 6-90

On clicking the Work tab, we can now see that the Auditor role has two cases to review. On clicking the highlighted Audit Review for the Quality department, we can see the Work Details page which also shows us the Audit Report pdf we created, which the Audit Report program linked to the Folder for the Case Documents for the Case.

A screenshot of an I B M case manager Mozilla firefox page. It lists content under the auditor review. A p d f file is highlighted under the A U D audit department 000000150002.

Figure 6-94

The Audit Report pdf for the Audit Review is clicked to open

The Audit Report pdf we created is highlighted in Figure 6-94, and on clicking this link, it launches the IBM Case Manager ViewOne Document Viewer which can be seen to be scrollable, so we can see the Logo and the Header page in the Viewer Frame.

A screenshot of an I B M case manager page. It displays the content of the A U D audit department 000000150002 p d f file.

Figure 6-95

The ViewOne Document Viewer is launched to display the pdf

Part 4 – Example 4 – Create an Auditor Calendar Table in a PDF Document

The following AuditTabTest.java test code provides an example of code to create a Table in a pdf document.
package com.ibm.filenet.ps.ciops.test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
// iText jar Library imports
import com.lowagie.text.Document;
import com.lowagie.text.Element;
import com.lowagie.text.Image;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfDocument;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.pdf.RandomAccessFileOrArray;
import com.lowagie.text.pdf.codec.TiffImage;
public class AuditTabTest {
     public static void main(String args[]) {
          //Set up the Date / Time system variables
          LocalDate AuditReportDate = LocalDate.now();
          LocalDateTime AuditReportDateTime = LocalDateTime.now();
          LocalTime AuditReportTime = LocalTime.now();
          DateTimeFormatter AuditDateFormat = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
          String sAuditDate = AuditDateFormat.format(AuditReportDateTime);
          OutputStream AuditPDFfile = null;
          try {
               AuditPDFfile = new FileOutputStream(new File("/opt/AuditReport/AuditTestTable.pdf"));
               //Create a new Audit Document object
               Document audit_document = new Document();
               //You need the iText PdfWriter class for a PDF document
               PdfWriter.getInstance(audit_document, AuditPDFfile);
               //Open the Audit document for writing a PDF
               audit_document.open();
               //Write the test content
               audit_document.add(new Paragraph("       Audit Report: Document Test - PDF Table Generation"));
          //Set Table Column widths (Relative)
          float[] columnWidths = {2, 2, 2, 2, 2};
          PdfPTable table = new PdfPTable(columnWidths);
    //Set the Table as 95% of the page width
     table.setWidthPercentage(95);
     table.getDefaultCell().setUseAscender(true);
     // Set up the Table data for each column of the Table
     String [] departmentNumber = {"0001","0002","0003","0004","0005"};
     String [] publishYes = {"Y","Y","Y","Y","Y"};
     String [] Auditor = {"N Smith","W Bloggs", "C Black", "F Jones" , "N Robinson"};
     String [] dateDetails1 = {"15-Aug-2022","18-Aug-2022", "20-Aug-2022", "25-Aug-2022" , "30-Aug-2022"};
     String [] departmentDetails2 = {"Quality","Electronics", "Warehouse" , "Engineering", "Engineering"};
     String [] managerDetails3 = {"W Bloggs","C Black", "F Jones" , "N Robinson", "N Smith"};
    int nStartQNO = 0;
    int nEndQNO   = 4;
     PdfPCell c1 = new PdfPCell(new Phrase("Department No."));
     c1.setHorizontalAlignment(Element.ALIGN_CENTER);
     table.addCell(c1);
     PdfPCell c2 = new PdfPCell(new Phrase("Auditor"));
     c2.setHorizontalAlignment(Element.ALIGN_CENTER);
     table.addCell(c2);
     PdfPCell c3 = new PdfPCell(new Phrase("Date"));
     c3.setHorizontalAlignment(Element.ALIGN_CENTER);
     table.addCell(c3);
     PdfPCell c4 = new PdfPCell(new Phrase("Department"));
     c4.setHorizontalAlignment(Element.ALIGN_LEFT);
     table.addCell(c4);
     PdfPCell c5 = new PdfPCell(new Phrase("Manager"));
     c5.setHorizontalAlignment(Element.ALIGN_LEFT);
    table.addCell(c5);
     table.setHeaderRows(1);
       //We have all the Questions loaded now
      int iCell = 0;
      int iRow = 0;
      String titleVal = null;
      int nQuestionColumns = 5;
          for  (String qRow:publishYes) {
               //Add the Table rows for each Element of the publishYes array
            if (iRow < 5 ) {
                    String [] cellRow = new String[nQuestionColumns];
                    cellRow[0] =  departmentNumber[iRow];
                    cellRow[1] =  Auditor[iRow];
                    cellRow[2] =  dateDetails1[iRow];
                    cellRow[3] =  departmentDetails2[iRow];
                    cellRow[4] =  managerDetails3[iRow];
                    //For each cell across the table row add the descriptions in the cellRow array
                      for (String sCell:cellRow){
                           titleVal =sCell;
                          table.addCell(sCell);
                          iCell ++;
                      }
                    iCell = 0;
                iRow ++;
            }
        }
          //Add the created table to the PDF Document
          audit_document.add(table);
          //close the document
          audit_document.close();
          System.out.println("Audit Report Table Created:" + sAuditDate);
          } catch (Exception e) {
               e.printStackTrace();
          } finally {
     //close the FileOutputStream
               try {
                    if (AuditPDFfile != null) {
                         AuditPDFfile.close();
                    }
               } catch (IOException io) {
                    //Failed to close
                    System.out.println("The Audit Table PDF file failed to close!");
                    }
               }
          }
     }
Listing 6-29

The AuditTabTest.java example source code for Table generation in a pdf file

Viewing the table shows the following output.

An audit report window is overlayed by the audit test table p d f window. It displays a table of audit report document test P D F. It lists department number, auditor, date, department, and manager.

Figure 6-96

The Test Table is output to the AuditTestTable.pdf file

Chapter 6 Exercises

The following questions cover the functions using the iText pdf creation package which we covered in this chapter.

MULTIPLE CHOICE QUESTIONS
  1. 1.
    The white cross on a red background icon against the IBM Case Builder Audit Master Solution panel means
    1. a)

      The Audit Master solution has new Solution edits to be saved.

       
    2. b)

      The Audit Master solution has been successfully deployed.

       
    3. c)

      The Audit Master solution failed to be deployed.

       
    4. d)

      The Audit Master solution has warning validation errors.

       
     
  2. 2.
    An abstract class, like XMLConfigReader
    1. a)

      Can be used by a subclass which can extend multiple abstract classes

       
    2. b)

      Can be used by a subclass which can only be used to extend the XMLConfigReader class

       
    3. c)

      Can be used by a subclass which could then only implement a single interface class

       
    4. d)

      Can be used by a subclass which can still extend multiple interface classes

       
     
  3. 3.
    A Java utility ResourceBundle
    1. a)

      Is often used to retrieve locale-specific String objects

       
    2. b)

      Is often used to retrieve a list of System properties

       
    3. c)

      Is often used to retrieve the Java classpath

       
    4. d)

      Is often used to retrieve the System Platform type

       
     
  4. 4.
    A PDF metadata Header is used to
    1. a)

      Hold the full layout of the pdf file as a Contents page

       
    2. b)

      Hold attributes such as the pdf author name, title, and a file description

       
    3. c)

      Hold the full security access to the pdf document

       
    4. d)

      Hold the list of Fonts displayed in the pdf document

       
     
MULTIPLE CHOICE ANSWERS
  1. 1.

    c) The Audit Master solution failed to be deployed.

     
  2. 2.

    d) Can be used by a subclass which can still extend multiple interface classes.

     
  3. 3.

    a) Is often used to retrieve locale-specific String objects.

     
  4. 4.

    b) Hold attributes such as the pdf author name, title, and a file description.

     
QUESTIONS
  1. 1.

    Describe the supporting libraries you need to run the Audit Report Java system.

     
  2. 2.

    What Java class object elements does the iText version 2.1.7 support for building the Audit Report pdf generation program?

     
  3. 3.

    What else could you use the pdf Table creation code for to extend the Auditing solution?

     
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.132.123