Custom File Upload Quicklink

Which IIQ version are you inquiring about?

8.4p2

Hi,
I wanted to make a custom quicklink to be able to upload file from local onto server.
I am stuck at being able to execute method on form submission but file is nowhere to be found. I’ve put two separate buttons for test purpose, but none reads the file.

Here’s my form xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:c="http://java.sun.com/jstl/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:t="http://myfaces.apache.org/tomahawk"
      xmlns:sp="http://sailpoint.com/ui"
      xmlns:p="http://primefaces.org/ui">

<h:body>
  <ui:composition template="/ngAppPage.xhtml">
    <ui:define name="body">
      <h:form enctype="multipart/form-data">

        <p:panel header="Upload Section"
                 style="padding:1.5rem; background-color:#f9f9f9; border:1px solid #ccc; border-radius:8px; box-shadow:0 0 5px rgba(0,0,0,0.1);">

          <f:facet name="header">
            <h:outputText value="File Upload Section"
                          style="font-size:2rem; color:#037DA1; font-weight:bold;" />
          </f:facet>

          <h:panelGrid columns="1" style="row-gap:1rem;">

            <h:outputLabel value="File Upload to Server"
                           style="font-weight:bold; font-size:1.2rem; color:#333;" />
            <div>
              <h:outputLabel for ="server" value="Server Name : "/>
              <h:inputText id="server" value="ASD TEST" label="server" required="true" requireMessage="Please provide server name"/>
            </div>

            <div>
              <h:outputLabel value="Tomahawk Fileupload : "/>
            <t:inputFileUpload value="#{uploadNICFile.uploadedFile}"
                               uploadThresholdSize="1000"
                               valueChangeListener="#{uploadNICFile.upload}"/>

            <t:commandButton value="Submit"
                             action="#{uploadNICFile.saveAction}"
                             onmouseover="this.style.backgroundColor='#005ea3';"
                             onmouseout="this.style.backgroundColor='#007ad9';" />
            </div>

            <div>
            <h:outputLabel value="Primefaces Fileupload : "/>
            <p:fileUpload id="localizationFileupload" value="#{uploadNICFile.uploadedFile}" mode="simple" valueChangeListener="#{uploadNICFile.uploadedFile}" accept="text/csv"
                          sizeLimit="#{systemConfig.maxUploadSize * 1000 * 1000}"/>
              <h:commandButton styleClass="primaryBtn" action="#{uploadNICFile.saveAction}" value="Submit but primefaces" />
            </div>

          </h:panelGrid>

        </p:panel>

        <h:messages id="messages" style="margin-top:1rem; font-size:0.95rem; color:#444;" />

      </h:form>
    </ui:define>
  </ui:composition>
</h:body>
</html>

Here’s my bean Class:

package org.object;

import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.file.UploadedFile;

import javax.faces.bean.SessionScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.event.ValueChangeEvent;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

@ManagedBean(name="uploadNICFile")
@SessionScoped
public class UploadNICFile {

    private String targetFilePath;
    private String username;
    private String pwd;
    private String server;
    private UploadedFile uploadedFile;

    public UploadedFile getUploadedFile(){
        return this.uploadedFile;
    }

    public void setUploadedFile(UploadedFile uploadedFile){
        this.uploadedFile = uploadedFile;
    }

    public String getServer() {
        return server;
    }

    public void setServer(String server) {
        this.server = server;
    }

    public void cancelAction(){
        this.uploadedFile = null;
        this.targetFilePath = null;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void upload(ValueChangeEvent event){
        Logger log = LogManager.getLogger("tempFile.Logger");
        //setUploadedFile(event.getNewValue());
        log.error("In upload method, event.getValue = " +event.getNewValue() );
        byte[] contents = uploadedFile.getContent();
        log.error("In upload method, contents = " +contents );
        log.error("In upload method, server = " +this.server );
        this.username = new String(contents);

    }

    public String saveAction() {


        Logger log = LogManager.getLogger("tempFile.Logger");
        log.error("In Upload Action method, uploadedfile  = " +this.uploadedFile);
        targetFilePath ="D:\\Uploaded_NIC_File.txt";
        if (uploadedFile != null && targetFilePath != null) {

            String destinationPath =  targetFilePath;
            try (InputStream input = uploadedFile.getInputStream();
                 OutputStream output = new FileOutputStream(targetFilePath)) {

                byte[] buffer = new byte[1024];
                int bytesRead;

                while ((bytesRead = input.read(buffer)) != -1) {
                    output.write(buffer, 0, bytesRead);
                    log.error("In try block of saveAction method, bytesRead = " +bytesRead);
                }

                //log.debug("File uploaded to "+targetFilePath);

            } catch (IOException e) {
                log.error("IO EXCEPTION whatever");
                //log.error("Upload failed: " + e.getMessage());
            }
        }
        return "/identityiq/home.jsf";
    }


}

Bean entry in faces-config

<!--  ***************************************************** -->

  <!--  File Upload                                           -->

  <!--  ***************************************************** -->

  <managed-bean>
    <managed-bean-name>uploadNICFile</managed-bean-name>
    <managed-bean-class>org.object.UploadNICFile</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>
    <navigation-rule>
      <description>uploadNICFile</description>
      <from-view-id>*</from-view-id>
      <navigation-case>
          <from-outcome>UploadNICFile</from-outcome>
          <to-view-id>uploadNicFileForm.xhtml</to-view-id>
          <redirect />
      </navigation-case>
    </navigation-rule>


  <!--  ***************************************************** -->

I am only getting

"In Upload Action method, uploadedfile  = null"

form both buttons

The issue WHich I find in your code is there is a mismatch,You’re using t:inputFileUpload and p:fileUpload on the same form, but your bean property is typed as:

private org.primefaces.model.file.UploadedFile uploadedFile;

The t:inputFileUpload binds to org.apache.myfaces.custom.fileupload.UploadedFile, so when when tries to call setUploadedFile(), the type doesn’t match, and the value never gets ses. You have to pick one content


import org.apache.myfaces.custom.fileupload.UploadedFile;

private UploadedFile uploadedFile;

    

And remove the p:fileUpload component from your XHTML entirely.

@blazejbadzio I am not sure but having both Tomahawk and PrimeFaces tags in the single form could cause issues. When i worked on a similar project in 7.x (see the image for namespaces i used), I didn’t use PrimeFaces, i made it work with Tomahawk but in later versions, you need to use PrimeFaces only. Architecture has been changed.

I checked latest version artefacts and not able to find tomahawk reference in xhtml, but primefaces are there. Sample file: appPage.xhtml

Also check if there are sufficient permissions to write in D drive which you are trying.

Note: Found a fix?Help the community by marking the comment as solution. Feel free to react(:heart:,:+1:, etc.)with an emoji to show your appreciation or message me directly if your problem requires a deeper dive.

Tried both solution from You and from @naveenkumar3 , neither worked. The access to target directory doesn’t matter since i am failing on “having the file” not saving it.

can you show me the code, which you tried after removing “remove the p:fileUpload component from your XHTML entirely” and after updating the import statement.

@blazejbadzio Could you please confirm if the artefacts you shared above is the complete set? If something is missing or additional object are there, please share. Will configure and test it out. thanks.

The xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:c="http://java.sun.com/jstl/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:t="http://myfaces.apache.org/tomahawk"
      xmlns:sp="http://sailpoint.com/ui"
      xmlns:p="http://primefaces.org/ui">

<h:body>
  <ui:composition template="/ngAppPage.xhtml">
    <ui:define name="body">
      <h:form enctype="multipart/form-data">

        <p:panel header="Upload Section"
                 style="padding:1.5rem; background-color:#f9f9f9; border:1px solid #ccc; border-radius:8px; box-shadow:0 0 5px rgba(0,0,0,0.1);">

          <f:facet name="header">
            <h:outputText value="File Upload Section"
                          style="font-size:2rem; color:#037DA1; font-weight:bold;" />
          </f:facet>

          <h:panelGrid columns="1" style="row-gap:1rem;">

            <h:outputLabel value="File Upload to Server"
                           style="font-weight:bold; font-size:1.2rem; color:#333;" />
            <div>
              <h:outputLabel for ="server" value="Server Name : "/>
              <h:inputText id="server" value="ASD TEST" label="server" required="true" requireMessage="Please provide server name"/>
            </div>

            <div>
              <h:outputLabel value="Tomahawk Fileupload : "/>
            <t:inputFileUpload value="#{uploadNICFile.uploadedFile}"
                               uploadThresholdSize="1000"
                               valueChangeListener="#{uploadNICFile.upload}"/>

            <t:commandButton value="Submit"
                             action="#{uploadNICFile.saveAction}"
                             styleClass="primaryBtn"
                             onmouseover="this.style.backgroundColor='#005ea3';"
                             onmouseout="this.style.backgroundColor='#007ad9';" />
            </div>


          </h:panelGrid>

        </p:panel>

        <h:messages id="messages" style="margin-top:1rem; font-size:0.95rem; color:#444;" />

      </h:form>
    </ui:define>
  </ui:composition>
</h:body>
</html>

The class

package org.object;

import org.primefaces.event.FileUploadEvent;
import org.apache.myfaces.custom.fileupload.UploadedFile;

import javax.faces.bean.SessionScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.event.ValueChangeEvent;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

@ManagedBean(name="uploadNICFile")
@SessionScoped
public class UploadNICFile {

    private String targetFilePath;
    private String username;
    private String pwd;
    private String server;
    private UploadedFile uploadedFile;

    public UploadedFile getUploadedFile(){
        return this.uploadedFile;
    }

    public void setUploadedFile(UploadedFile uploadedFile){
        this.uploadedFile = uploadedFile;
    }

    public String getServer() {
        return server;
    }

    public void setServer(String server) {
        this.server = server;
    }

    public void cancelAction(){
        this.uploadedFile = null;
        this.targetFilePath = null;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void upload(ValueChangeEvent event){
        Logger log = LogManager.getLogger("tempFile.Logger");
        //setUploadedFile(event.getNewValue());
        log.error("In upload method, event.getValue = " +event.getNewValue() );
        byte[] contents = null;
        try {
            contents = uploadedFile.getBytes();
        } catch (IOException e) {
            log.error("Exception class = " + e.getClass() );
        }
        log.error("In upload method, contents = " +contents );
        log.error("In upload method, server = " +this.server );
        this.username = new String(contents);

    }

    public String saveAction() {


        Logger log = LogManager.getLogger("tempFile.Logger");
        log.error("In Upload Action method, uploadedfile  = " +this.uploadedFile);
        targetFilePath ="D:\\Uploaded_NIC_File.txt";
        if (uploadedFile != null && targetFilePath != null) {

            String destinationPath =  targetFilePath;
            try (InputStream input = uploadedFile.getInputStream();
                 OutputStream output = new FileOutputStream(targetFilePath)) {

                byte[] buffer = new byte[1024];
                int bytesRead;

                while ((bytesRead = input.read(buffer)) != -1) {
                    output.write(buffer, 0, bytesRead);
                    log.error("In try block of saveAction method, bytesRead = " +bytesRead);
                }

                //log.debug("File uploaded to "+targetFilePath);

            } catch (IOException e) {
                log.error("IO EXCEPTION whatever");
                //log.error("Upload failed: " + e.getMessage());
            }
        }
        return "/identityiq/home.jsf"; // stay on the same page
    }


}

The rest of the code remains the same

There’s also a simple workflow but i don’t thing it’s of any issue since it’s a simple one.

Workflow

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Workflow PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Workflow explicitTransitions="true" name="Workflow-Upload-NIC-file" type="UpdateIdentity">
  <Variable initializer="true" name="transient"/>
  <Variable input="true" name="requester"/>
  <Variable input="true" name="lastApprovalState"/>
  <Variable input="true" name="approved"/>
  <Variable initializer="Upload NIC file" name="flow"/>
  <Variable initializer="false" name="optimisticProvisioning"/>
  <Variable initializer="spadmin" name="fallbackApprover">
    <Description>A String that specifies the name of the Identity that will
      be assigned any approvals where the owner of the approver
      can&amp;#39;t be resolved. Example if the scheme is &amp;quot;owner&amp;quot; and the
      application doesn&amp;#39;t specify and owner.</Description>
  </Variable>
  <Step icon="Start" name="Start" posX="28" posY="10">
    <Approval mode="serial" name="NIC File Upload Form" renderer="uploadNicFileForm.xhtml" owner="ref:requester">
      <Arg name="workItemType" value="Form"/>
      <Arg name="workItemForm" value="string:Form-Upload-NIC-file"/>
      <Arg name="workItemType" value="Generic"/>
    </Approval>
    <Transition to="Stop" when="approved"/>
  </Step>
  <Step icon="Stop" name="Stop" posX="508" posY="10"/>
</Workflow>

Dynamic Scope

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE DynamicScope PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<DynamicScope allowAll="true" name="Upload NIC file - DynamicScope">
  <Description></Description>
  <PopulationRequestAuthority>
    <MatchConfig enableAttributeControl="true">
      <IdentityAttributeFilterControl operation="or">
        <IdentityAttributeFilterControl displayName="Name" name="displayName"/>
        <IdentityAttributeFilterControl displayName="EmployeeID" name="employeeID"/>
      </IdentityAttributeFilterControl>
    </MatchConfig>
  </PopulationRequestAuthority>
</DynamicScope>

Quicklink

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE QuickLink PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<QuickLink action="workflow" category="Tasks"  messageKey="Upload NIC File" name="Upload NIC File" >
  <Attributes>
    <Map>
      <entry key="requester">
        <value>
          <Script>
            <Source>return currentUser;</Source>
          </Script>
        </value>
      </entry>
      <entry key="workflowName" value="Workflow-Upload-NIC-file"/>
      <entry key="workflowSuccess"/>
    </Map>
  </Attributes>
  <QuickLinkOptions allowSelf="true" >
    <DynamicScopeRef>
      <Reference class="sailpoint.object.DynamicScope" name="Upload NIC file - DynamicScope"/>
    </DynamicScopeRef>
  </QuickLinkOptions>
</QuickLink>

I think that’s all there is

Thanks. Let me try to configure and share my findings. Will also ping you over chat for any quick info.

Multipart Filter Not Configured

Check your web.xml for this filter:

<!-- For Tomahawk file upload -->
<filter>
    <filter-name>MyFacesExtensionsFilter</filter-name>
    <filter-class>org.apache.myfaces.webapp.filter.ExtensionsFilter</filter-class>
    <init-param>
        <param-name>maxFileSize</param-name>
        <param-value>20m</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>MyFacesExtensionsFilter</filter-name>
    <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>

Without this filter, t:inputFileUpload will NEVER bind the file — it stays null regardless of type.


Wrong Import After Type Fix

import org.apache.myfaces.custom.fileupload.UploadedFile;
// NOT org.primefaces.model.file.UploadedFile

enctype on h:form

The enctype="multipart/form-data" must be set correctly. With Tomahawk, use:

<!-- Change this -->
<h:form enctype="multipart/form-data">

<!-- To this (Tomahawk specific attribute) -->
<h:form enctype="multipart/form-data" id="uploadForm">

Remove the valueChangeListener from t:inputFileUpload

<!-- Remove valueChangeListener, it's not needed for file upload -->
<t:inputFileUpload 
    value="#{uploadNICFile.uploadedFile}"
    uploadThresholdSize="1000"/>
    <!-- Remove: valueChangeListener="#{uploadNICFile.upload}" -->

try this and please share your web.xml if the issue is still ther

Hi @blazejbadzio , I faced a similar issue 4 years ago. Let me tell you what we did for this scenario. When the CSV file is uploaded into the plugin, we converted the CSV file to JSON before calling the API instead of directly sending raw data file. Send the JSON data into the API. Without any issue, it is working. You can try this approach if you are still unable to upload the raw file and read it in the API Java file. I hope this will be handled smoothly.

Thanks,

PVR>

@blazejbadzio I was occupied with other tasks. Please let me know if this issue is still open, we can connect over chat for a quick validation.

@blazejbadzio As discussed here are the code snippets:

faces-config-snippet


  <managed-bean>
    <managed-bean-name>uploadNICFile</managed-bean-name>
    <managed-bean-class>org.object.UploadNICFile</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>
    <navigation-rule>
      <description>uploadNICFile</description>
      <from-view-id>/workitem/uploadNicFileForm.xhtml</from-view-id>
      <navigation-case>
          <from-outcome>UploadNICFile</from-outcome>
          <to-view-id>/workitem/uploadNicFileForm.xhtml</to-view-id>
          <redirect />
      </navigation-case>
      <navigation-case>
          <from-outcome>success</from-outcome>
          <to-view-id>/home.jsf</to-view-id>
          <redirect />
      </navigation-case>
    </navigation-rule>


uploadNicFileForm

<ui:component
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html">

  <style type="text/css">
    .upload-container {
        padding: 20px;
        background-color: #f9f9f9;
        border: 1px solid #ddd;
        border-radius: 8px;
        font-family: 'Inter', sans-serif;
    }
    .upload-header {
        font-size: 24px;
        color: #037DA1;
        font-weight: bold;
        margin-bottom: 20px;
    }
    .upload-field {
        margin-bottom: 15px;
    }
    .upload-label {
        font-weight: 600;
        margin-right: 10px;
        color: #333;
    }
    .upload-box {
        border: 2px dashed #037DA1;
        padding: 30px;
        text-align: center;
        border-radius: 6px;
        background: #fff;
        margin-bottom: 20px;
    }
    .btn-group {
        margin-top: 20px;
    }
  </style>

  <h:form id="uploadForm" enctype="multipart/form-data">

    <div class="upload-container">
      <div class="upload-header">NIC File Upload</div>

      <div class="upload-field">
        <h:outputLabel for="server" value="Server Instance: " styleClass="upload-label"/>
        <h:inputText id="server" value="#{uploadNICFile.server}" 
                     style="width:300px; padding:8px; border:1px solid #ccc; border-radius:4px;"/>
      </div>

      <div class="upload-box">
        <h:outputLabel value="Select File to Upload" style="font-weight:bold; display:block; margin-bottom:15px; font-size:16px;"/>
        <!-- Native JSF 2.2 file upload -->
        <h:inputFile id="nicFile" 
                     value="#{uploadNICFile.uploadedFile}"
                     style="margin:auto; display:inline-block;"/>
      </div>

      <div class="btn-group">
        <h:commandButton id="submitBtn"
                         value="Save and Complete"
                         action="#{uploadNICFile.saveAction}"
                         styleClass="primaryBtn"
                         style="padding:12px 30px; border-radius:4px; cursor:pointer; font-weight:bold;"/>
        
        <h:commandButton value="Cancel"
                         action="#{uploadNICFile.cancelAction}"
                         immediate="true"
                         styleClass="secondaryBtn"
                         style="margin-left:15px; padding:12px 30px; border-radius:4px; cursor:pointer;"/>
      </div>

      <h:messages id="messages" globalOnly="false" layout="list"
                  infoStyle="color:green; margin-top:15px;" 
                  errorStyle="color:red; margin-top:15px;"
                  style="font-family:sans-serif; list-style-type:none; padding-left:0;"/>

    </div>

  </h:form>

</ui:component>

Workflow

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Workflow PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Workflow created="1775718388251" explicitTransitions="true" id="7f0000019d701d0e819d7110521b2dd9" modified="1775721074783" name="Community-Workflow-Upload-NIC-file" significantModified="1775721074783" type="UpdateIdentity">
  <Variable initializer="true" name="transient"/>
  <Variable input="true" name="requester"/>
  <Variable input="true" name="lastApprovalState"/>
  <Variable input="true" name="approved"/>
  <Variable initializer="Upload NIC file" name="flow"/>
  <Variable initializer="false" name="optimisticProvisioning"/>
  <Variable initializer="spadmin" name="fallbackApprover">
    <Description>A String that specifies the name of the Identity that will
      be assigned any approvals where the owner of the approver
      can&amp;#39;t be resolved. Example if the scheme is &amp;quot;owner&amp;quot; and the
      application doesn&amp;#39;t specify and owner.</Description>
  </Variable>
  <Step icon="Start" name="Start" posX="28" posY="10">
    <Approval mode="serial" name="NIC File Upload Form" owner="ref:requester" renderer="uploadNicFileForm.xhtml">
      <Arg name="workItemType" value="Test"/>
    </Approval>
    <Transition to="Stop" when="approved"/>
  </Step>
  <Step icon="Stop" name="Stop" posX="508" posY="10"/>
</Workflow>

UploadNICFile.java

package org.object;

import javax.faces.context.FacesContext;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.servlet.http.Part;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.File;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

import sailpoint.api.SailPointContext;
import sailpoint.api.SailPointFactory;
import sailpoint.object.WorkItem;

@ManagedBean(name="uploadNICFile")
@SessionScoped
public class UploadNICFile {

    private String server;
    private Part uploadedFile;

    private static final Logger log = LogManager.getLogger("tempFile.Logger");

    public UploadNICFile() {
        System.out.println("UploadNICFile bean instantiated.");
    }

    public Part getUploadedFile(){
        return this.uploadedFile;
    }

    public void setUploadedFile(Part uploadedFile){
        this.uploadedFile = uploadedFile;
    }

    public String getServer() {
        return server;
    }

    public void setServer(String server) {
        this.server = server;
    }

    public void cancelAction(){
        System.out.println("cancelAction() called.");
        this.uploadedFile = null;
    }

    public String saveAction() {
        System.out.println("saveAction() ENTERED.");
        log.info("saveAction() triggered.");
        log.info("Server value = " + this.server);
        log.info("UploadedFile (Part) = " + (this.uploadedFile != null ? this.uploadedFile.getSubmittedFileName() : "null"));

        try {
            if (uploadedFile != null) {
                String fileName = uploadedFile.getSubmittedFileName();
                
                // Clean filename from path characters
                if (fileName != null) {
                    if (fileName.contains("\\")) {
                        fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);
                    } else if (fileName.contains("/")) {
                        fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
                    }
                } else {
                    fileName = "uploaded_file_" + System.currentTimeMillis();
                }

                String baseDir = "<YourDirectory Path>";
                File dir = new File(baseDir);
                if (!dir.exists()) {
                    dir.mkdirs();
                }

                File targetFile = new File(dir, fileName);
                System.out.println("Attempting to save to " + targetFile.getAbsolutePath());
                log.info("Saving file to: " + targetFile.getAbsolutePath());

                try (InputStream input = uploadedFile.getInputStream();
                     OutputStream output = new FileOutputStream(targetFile)) {

                    byte[] buffer = new byte[8192];
                    int bytesRead;
                    while ((bytesRead = input.read(buffer)) != -1) {
                        output.write(buffer, 0, bytesRead);
                    }
                    System.out.println("File saved successfully: " + targetFile.length() + " bytes");
                    log.info("File saved: " + targetFile.length() + " bytes");
                    
                    try {
                        SailPointContext context = SailPointFactory.getCurrentContext();
                        // Retrieve the Work Item ID from the request parameter
                        String workItemId = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id");
                        
                        if (workItemId != null) {
                            WorkItem wi = context.getObjectById(WorkItem.class, workItemId);
                            if (wi != null) {
                                System.out.println("Completing Work Item " + workItemId);
                                log.info("Automatically completing Work Item: " + workItemId);
                                wi.setState(WorkItem.State.Finished);
                                wi.setAttribute("approved", true); // Signal workflow transition
                                context.saveObject(wi);
                                context.commitTransaction();
                            }
                        }
                    } catch (Exception ex) {
                        System.err.println("Work Item Completion failed: " + ex.getMessage());
                        log.error("Error during automatic Work Item completion: " + ex.getMessage());
                    }

                    this.uploadedFile = null;
                    return "success";
                }
            } else {
                System.out.println("uploadedFile (Part) is NULL in saveAction.");
            }
        } catch (Throwable t) {
            System.err.println(" CRITICAL ERROR in saveAction():");
            t.printStackTrace();
            log.error("Failed to save file: " + t.getMessage(), t);
        }
        
        return null;
    }
}

Thanks a Milion @neel193 !!