Question
Marcio Coelho · Jan 27

<REMOTE EXECUTE INVALID WRITE>: Iris with jdbc driver

Hi....
I am trying to execute legacy routines from Cache 2018, into new environment with Iris 2021. I use new JDBC driver to make this connection, and change my java code to execute this legacy  routines. But I get this write error: <REMOTE EXECUTE INVALID WRITE> 
I changed the mnemonic routine to populate object and return this object to java. And the java class convert this object to json.
This is my simple classes used for this process, just to exemplification:

Java Class

package test;

import java.sql.SQLException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.intersystems.jdbc.IRIS;
import com.intersystems.jdbc.IRISConnection;
import com.intersystems.jdbc.IRISDataSource;
import com.intersystems.jdbc.IRISObject;

public class Reader {

    public static final String CACHE_CLASS_NAME = "Utils.CSW1JavaFunctions";
    public IRISConnection connection;
    public IRIS iris;

    public Reader(IRISConnection connection) throws SQLException {
        this.connection = connection;
        this.iris = IRIS.createIRIS(connection);
    }

    public static void main(String[] args) throws SQLException {
        IRISDataSource dataSource = new IRISDataSource();
        dataSource.setServerName("localhost");
        dataSource.setPortNumber(1972);
        dataSource.setDatabaseName("TEST");
        dataSource.setUser("_SYSTEM");
        dataSource.setPassword("xxxxxxxxx");

        IRISConnection connection = (IRISConnection) dataSource.getConnection();
        Reader reader = new Reader(connection);

        try {
            JsonNode jsonNode = reader.connect("IrisWrite", "param1", "param1");
            System.out.println(jsonNode.toString());
        } catch (Exception exc) {
            exc.printStackTrace();
        }

    }

    public JsonNode connect(String method, Object... _args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode jsonNode = null;
        try {
            IRISObject data  = (IRISObject) iris.classMethodObject(CACHE_CLASS_NAME, method, _args);
            String string = (String) data.invoke("%ToJSON");
            data.close();
            jsonNode = (JsonNode) mapper.readTree(string);
            return jsonNode;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

}

Iris class:
 

ClassMethod IrisWrite(args...) As %RegisteredObject
{
 set %return = ##class(%ZEN.proxyObject).%New()
 set %return.lines = ##class(%ListOfDataTypes).%New() 
 try {
  ;
  use $io::"^%SMMNEMONIC" 
  ;
  write args(1)
  write args(2)
  ;
  set regObj = ##class(%ZEN.Auxiliary.altJSONProvider).%ObjectToAET(%return,,,"d")
  ;
  return regObj
 catch ex {
  throw ex
 }
}

//Mnemonic routine:
%SMMNEMONIC ;
;
wstr(string) ; 
    set string=$zstrip($zstrip(string,"<>W"),"*C")
    quit:string=""
    do %return.lines.Insert(string)
    quit

This error is new to me.
How to populate a object with write command? 
 

Product version: IRIS 2021.1
$ZV: IRIS for Windows (x86-64) 2021.1 (Build 215U) Wed Jun 9 2021 09:39:22 EDT
0
0 116
Discussion (3)1
Log in or sign up to continue

Do not use %ZEN, it’s deprecated. There is Native JSON support, and %JSON.Adaptor, use this instead. Any output with write command will produce this error. 

Hi @Maslennikov.Dmitry... 
I get the error on execute the line with "write args(1)" after "use $io..."
Not possible execute "write" from process with JDBC connection.
I will change all %Zen from my code coming soon. Thanks for warning

You can't call native api methods which write to device as is.

If you need to call some piece of code which writes to device use this wrapper:

/// Executes and returns device output 
/// pObj - OREF or class
/// pMethod - instance or class method to execute respectively
/// pArgs - additional arguments
ClassMethod OutputToStr(pObj, pMethod, pArgs...) As %String [ ProcedureBlock = 0 ]
{
	set tOldIORedirected = ##class(%Device).ReDirectIO()
	set tOldMnemonic = ##class(%Device).GetMnemonicRoutine()
	set tOldIO = $io
	try {
		set str=""

		//Redirect IO to the current routine - makes use of the labels defined below
		use $io::("^"_$ZNAME)

		//Enable redirection
		do ##class(%Device).ReDirectIO(1)

		if $isobject(pObj) {
			do $Method(pObj,pMethod,pArgs...)
		} elseif $$$comClassDefined(pObj) {
			do $ClassMethod(pObj,pMethod,pArgs...)
		}
	} catch ex {
		set str = ""
	}

	//Return to original redirection/mnemonic routine settings
	if (tOldMnemonic '= "") {
		use tOldIO::("^"_tOldMnemonic)
	} else {
		use tOldIO
	}
	do ##class(%Device).ReDirectIO(tOldIORedirected)

	quit str

	//Labels that allow for IO redirection
	//Read Character - we don't care about reading
rchr(c)      quit
	//Read a string - we don't care about reading
rstr(sz,to)  quit
	//Write a character - call the output label
wchr(s)      do output($char(s))  quit
	//Write a form feed - call the output label
wff()        do output($char(12))  quit
	//Write a newline - call the output label
wnl()        do output($char(13,10))  quit
	//Write a string - call the output label
wstr(s)      do output(s)  quit
	//Write a tab - call the output label
wtab(s)      do output($char(9))  quit
	//Output label - this is where you would handle what you actually want to do.
	//  in our case, we want to write to str
output(s)    set str=str_s   quit
}

So in your case it would be something like:

IRISObject data  = (IRISObject) iris.classMethodObject(CACHE_CLASS_NAME, method, _args);
String string = iris.classMethodString("SomeClass", "OutputToStr", data, "ToJSON")
data.close();