docs: String copyFile(
              final ExecutionContext executionContext,
              final File file,
              final InputStream input,
              final String content,
              final INodeEntry node,
              String destination
      ) throws FileCopierException
      {
          final String pluginname = getProvider().getName();
          final Map<String, Map<String, String>> localDataContext = createScriptDataContext(
              executionContext.getFramework(),
              executionContext.getFrameworkProject(),
              executionContext.getDataContext());
          //add node context data
          localDataContext.put("node", DataContextUtils.nodeData(node));
  
          Description pluginDesc = getDescription();
          //load config.* property values in from project or framework scope
          final Map<String, Map<String, String>> finalDataContext;
          try {
              finalDataContext = loadConfigData(executionContext, loadInstanceDataFromNodeAttributes(node, pluginDesc), localDataContext, pluginDesc,
                                                ServiceNameConstants.FileCopier
              );
          } catch (ConfigurationException e) {
              throw new FileCopierException("[" + pluginname + "]: "+e.getMessage(),
                      StepFailureReason.ConfigurationFailure, e);
          }
  
          final File srcFile =
                  null != file ?
                          file :
                          BaseFileCopier.writeTempFile(executionContext, null, input, content);
  
          String destFilePath = destination;
          if (null == destFilePath) {
  
              String identity = null!=executionContext.getDataContext() && null!=executionContext.getDataContext().get("job")?
                                executionContext.getDataContext().get("job").get("execid"):null;
              destFilePath = BaseFileCopier.generateRemoteFilepathForNode(
                      node,
                      executionContext.getFramework().getFrameworkProjectMgr().getFrameworkProject(executionContext.getFrameworkProject()),
                      executionContext.getFramework(),
                      (null != file ? file.getName() : "dispatch-script"),
                      null,
                      identity
              );
          }
          //put file in a directory
          if (null != destFilePath && destFilePath.endsWith("/")) {
              destFilePath += srcFile.getName();
          }
          //add some more data context values to allow templatized script-copy attribute
          final HashMap<String, String> scptexec = new HashMap<>();
          //set up the data context to include the local temp file
          scptexec.put("file", srcFile.getAbsolutePath());
          scptexec.put("destination", null != destFilePath ? destFilePath : "");
          finalDataContext.put("file-copy", scptexec);
          Map<String, Map<String, String>> fileCopyContext =
                  DataContextUtils.addContext(
                          "file-copy",
                          scptexec,
                          null
                  );
  
          final ExecArgList execArgList = createScriptArgsList(fileCopyContext);
          final String localNodeOsFamily=getFramework().createFrameworkNode().getOsFamily();
  
          executionContext.getExecutionListener().log(3,
                                                      "[" +
                                                      getProvider().getName() +
                                                      "] executing: " +
                                                      execArgList.asFlatStringList()
          );
  
          final ByteArrayOutputStream captureSysOut = new ByteArrayOutputStream();
          try {
              final int result = getScriptExecHelper().runLocalCommand(
                      localNodeOsFamily,
                      execArgList,
                      finalDataContext,
                      null,
                      captureSysOut,
                      System.err
              );
              executionContext.getExecutionListener().log(3, "[" + pluginname + "]: result code: " + result);
              if(result!=0){
                  throw new FileCopierException("[" + pluginname + "]: external script failed with exit code: " + result,
                                                NodeStepFailureReason.NonZeroResultCode);
              }
          } catch (IOException e) {
              throw new FileCopierException("[" + pluginname + "] " + e.getMessage(), StepFailureReason.IOFailure);
          } catch (InterruptedException e) {
              Thread.currentThread().interrupt();
              throw new FileCopierException("[" + pluginname + "] " + e.getMessage(), StepFailureReason.Interrupted);
          }finally {
              if(null == file) {
                  if (!ScriptfileUtils.releaseTempFile(srcFile)) {
                      executionContext.getExecutionListener().log(
                              Constants.WARN_LEVEL,
                              "Unable to remove local temp file: " + srcFile.getAbsolutePath()
                      );
                  }
              }
          }
  
  
          //load string of output from outputstream
          final String output = captureSysOut.toString();
          if (null == output || output.length() < 1) {
              if (null != destFilePath) {
                  return destFilePath;
              }
              throw new FileCopierException("[" + pluginname + "]: No output from external script",
                                            ScriptPluginFailureReason.ScriptPluginFileCopierOutputMissing
              );
          }
          //TODO: require any specific format for the data?
          //look for first line of output
          final String[] split1 = output.split("(\\r?\\n)");
          if (split1.length < 1) {
              if (null != destFilePath) {
                  return destFilePath;
              }
              throw new FileCopierException("[" + pluginname + "]: No output from external script",
                                            ScriptPluginFailureReason.ScriptPluginFileCopierOutputMissing);
          }
          final String remotefilepath = split1[0];
  
          executionContext.getExecutionListener().log(3, "[" + pluginname + "]: result filepath: " + remotefilepath);
  
          return remotefilepath;
      }