Question
Marcio Coelho · Nov 30, 2021

Iris vs Java

Hello. I'm having problems migrating a java application that is running with cache 2018 to Iris
The application uses json generated by the cache to receive it as a string in java.

Basically an instance of the "%ZEN.proxyObject" class
On Iris I've been trying to convert this '%ZEN.proxyObject Object to an instance of the %GlobalBinaryStream class and I need to get it via reference.
But I haven't had success yet. I Just found example with primitive dataTypes via reference and I need a Streams.

Has anyone done something like that?
This is my cache class example:

ClassMethod Login(ByRef streamReturn As %GlobalBinaryStream, user As %String, pass As %String) As %Status [ ProcedureBlock = 1 ]
{
 set streamReturn=##class(%GlobalBinaryStream).%New()
 set cswReturn=##class(%ZEN.proxyObject).%New()
 set cswReturn.success=1
 ;
 set sc=##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(.streamReturn,cswReturn,,,,"aelo")  ;
 quit sc
}

I need to get streamReturn with java native api...
Thanks 


 

Product version: IRIS 2021.1
$ZV: IRIS for UNIX (Red Hat Enterprise Linux for x86-64) 2021.1 (Build 215U) Wed Jun 9 2021 09:48:12 EDT
0
0 294
Discussion (11)3
Log in or sign up to continue

suggestion

ClassMethod Login(ByRef streamReturn As %GlobalBinaryStream, user As %String, pass As %String) As %Status [ ProcedureBlock = 1 ]
{
 set streamReturn=##class(%GlobalBinaryStream).%New()
 set cswReturn={}
 set cswReturn.success=1
 ;
 set format=##class(%JSON.Formatter).%New()
 set format.Indent=0
 set format.LineTerminator=""
 set sc=format.FormatToStreamFromObject(cswReturn,.streamReturn)  
 quit sc
}

or simply

ClassMethod Login(ByRef streamReturn As %GlobalBinaryStream, user As %String, pass As %String) As %Status [ ProcedureBlock = 1 ]
{
 set streamReturn=##class(%GlobalBinaryStream).%New() set cswReturn="{""success"":1}"
 set sc=streamReturn.Write(cswReturn) quit sc
}
Thanks for the reply Mr. Cemper.
But my application has much more complex json layouts. This was just a very basic example. From the tests I did with Stream, it is ok. My problem is how to get this stream via Java through a JDBC connection

If you have a complex object, then add %JSON.Adaptor to the class,
and do an %JSONExportToStream as you did with %XML.Adapter

The %GlobalBinaryStream class is deprecated and newly written code should instead use %Stream.GlobalBinary.  Of course, existing code that is not needing modification can continue using %Library.GlobalBinaryStream.
.  

Hi Marcio,

I am a developer at InterSystems and I work with Java, JSON, and SQL every day. Perhaps I can help. Can you provide an example of the Java code where you receive the JSON output from IRIS? I think I have a couple of interesting options for you.

-Dan

I created a very simple example with java classes and Iris methods. I make return a json string to demonstrate. I need to change this Strings to Strems. Ok?

I use quarkus and jdk 11 and Iris database

Restasy endpoint

@Path("/session")
@RequestScoped
public class SessionEndPoint {

	@Inject
	BrokerService brokerService;
	
	private ObjectMapper mapper = new ObjectMapper();

	@POST
	@Path("IrisReturn")
	@Produces(MediaType.APPLICATION_JSON)
	public Response systemBoot(@HeaderParam("user") String user, @HeaderParam("pass") String pass) {
		ResponseBuilder response = Response.ok();
		try {
			JsonNode serverReturn = (JsonNode) brokerService.execute("IrisReturn", user, pass);
			response = Response.ok(mapper.writeValueAsString(serverReturn));
		} catch (Exception ex) {
			response = Response.status(Status.INTERNAL_SERVER_ERROR);
		}

		return response.build();
	}

	
}

Class BrokerService with @Inject datasource and unwrap to IrisConnection

@RequestScoped
public class BrokerService {

    @Inject
    DataSource dataSource;

    private Connection conn;
    private IRIS iris;
    private static String CACHE_CLASS_NAME = "Utils.CSW1JavaFunctions";

    @PostConstruct
    public void linit() {

        try {
            conn = dataSource.getConnection();
            IRISConnection iconn = (IRISConnection) conn.unwrap(IRISConnection.class);
            iris = IRIS.createIRIS(iconn);
        } catch (Exception e) {
            System.err.println("Connection erro!");
            e.printStackTrace();
        }
    }

    public Object execute(String method, Object... args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode jsonNode = null;
        IRISReference cacheReturn = new IRISReference("");
        try {
            iris.classMethodStatusCode(CACHE_CLASS_NAME, method, cacheReturn, args[0], args[1]);
            String xxxxxx = mapper.writeValueAsString(cacheReturn.getValue());
            jsonNode = (JsonNode) mapper.readTree(xxxxxx);
        } catch (Exception ex) {
            ex.printStackTrace();
		}
        return jsonNode;
    }

}

Iris Method 
 

ClassMethod IrisReturn(ByRef cswStream As %String, user, pass) As %Status [ ProcedureBlock = 1 ]
{
 ;
 set cswStream=""
 //set cswStream=##class(%Stream.GlobalBinary).%New()
 
 ;
 set cswReturn=##class(%ZEN.proxyObject).%New()
 set cswReturn.user=$get(user)
 set cswReturn.pass=$get(pass)
 set cswReturn.success=1
 ;
 try {
 	set cswObj = ##class(%ZEN.Auxiliary.altJSONProvider).%ObjectToAET(cswReturn,,,"d")
 	set cswStream=cswObj.%ToJSON()
 } catch ex {
 	set ^%MJ("ee")=ex.AsStatus()
 }
 quit $$$OK
}


To test my example, I use curl
 

curl -i -X POST -H "user: marcio.coelho" -H "pass: 123" -H "Content-Type: application/json" http://localhost:8090/session/IrisReturn

Return
 

"{\"pass\":123,\"success\":1,\"user\":\"marcio.coelho\"}"

I need that parameter cswStream is a Stream. But.. I dont find examples in documentation to use Streams in java.
With you need the project, I can upload in my GitHub Account and share to you.
 

Interesting! I didn't see which version of InterSystems IRIS you are using but you might try a couple of things:

First, don't return a status value from your method. Instead return a %Stream.GlobalBinary instance. If you still want a status, get it some other way. I recommend just throwing an exception - IRIS Native should handle that okay. The object value you get back will be a proxy object that should allow you to read the stream. In our documentation you might search for "reverse proxy objects".

If get some time, I will try to build a sample of doing this.

Hi Dan... This is  $zv from Iris instance.

IRIS for UNIX (Red Hat Enterprise Linux for x86-64) 2021.1 (Build 215U) Wed Jun 9 2021 09:48:12 EDT

I think you meant "Inverse proxy Object" Here
I changed my code to this:
Iris Class

 Class Utils.CSW1JavaFunctions Extends %RegisteredObject [ ClassType = "", Not ProcedureBlock ] {

ClassMethod IrisReturn(user, pass) As %Stream.GlobalBinary [ ProcedureBlock = 1 ]
{
 ;
 set cswStream=##class(%Stream.GlobalBinary).%New()
 
 ;
 set cswReturn=##class(%ZEN.proxyObject).%New()
 set cswReturn.user=$get(user)
 set cswReturn.pass=$get(pass)
 set cswReturn.success=1
 ;
 try {
 	set cswObj = ##class(%ZEN.Auxiliary.altJSONProvider).%ObjectToAET(cswReturn,,,"d")
 	do cswObj.%ToJSON(cswStream)
 } catch ex {
 	throw ex
 }
 quit cswStream
}
}

I am not use Persistence class. In my case, my classes extends from %RegisteredObject
Change Java method to this:
 

    public Object execute(String method, Object... args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode jsonNode = null;
        try {

            
            byte[] data  = iris.classMethodBytes(CACHE_CLASS_NAME, method, args[0], args[1]);
            
            InputStream is = new ByteArrayInputStream(data);

            /* Just debug */
            BufferedReader in = new BufferedReader(new InputStreamReader(is));
            String line = null;
            while ((line = in.readLine()) != null) {
                System.out.println(line);
            }
            
            jsonNode = (JsonNode) mapper.readTree(is);
        } catch (Exception ex) {
            ex.printStackTrace();
		}
        return jsonNode;
    }

With this example, I get "3@%Stream.GlobalBinary" in console... lol :)
 

Yes, of course "inverse" - sorry.

Persistent vs RegisteredObject - not a problem but you are calling a simple class method so we don't need any super class. I used this implementation for the IRIS Class:

Class Utils.CSW1JavaFunctions
{
    ClassMethod IrisReturn(user = "user", pass = "pass") As %Stream.GlobalBinary
    {
         try {
             set cswStream=##class(%Stream.GlobalBinary).%New()
             set cswReturn = {"user":(user), "pass":(pass) }
             do cswReturn.%ToJSON(cswStream)
             return cswStream
         } catch exc {
             write !,"Caught Exception on server: ", exc.AsSQLMessage()
         }
    }
}

 

 

And this is a crude hack at the Java code - the anonymous InputStream class could use more work but it does run for this simple example. I'll leave the rest of the InputStream coding to you.

package utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.intersystems.jdbc.*;

import java.io.*;
import java.sql.SQLException;


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(51776);
        dataSource.setDatabaseName("USER");
        dataSource.setUser("_SYSTEM");
        dataSource.setPassword("SYS");
        IRISConnection connection = (IRISConnection) dataSource.getConnection();
        Reader reader = new Reader(connection);
        try {
            JsonNode jsonNode = reader.execute("IrisReturn", "java", "jpass");
            System.out.println(jsonNode.toString());
        } catch (Exception exc) {
            exc.printStackTrace();
        }
    }

    public JsonNode execute(String method, Object... args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode jsonNode = null;
        try {
            IRISObject data  = (IRISObject) iris.classMethodObject(CACHE_CLASS_NAME, method, args[0], args[1]);
            InputStream is = new InputStream() {
                byte[] buffer;
                int pos = 0;
                int len = -1;

                @Override
                public int read() throws IOException {
                    if (pos >= len) {
                        getBuffer();
                    }
                    if (len == -1) {
                      return -1;
                    }
                    return buffer[pos++];
                }

                void getBuffer() {
                    pos = 0;
                    IRISReference readLen = new IRISReference(3200);
                    String string = (String) data.invoke("Read", readLen);
                    if (readLen.getLong() == -1) {
                        buffer = null;
                        len = -1;
                    } else {
                        buffer = string.getBytes();
                        len = buffer.length;
                    }
                }
            };

            jsonNode = (JsonNode) mapper.readTree(is);
            return jsonNode;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
}

Running this produces this output:

/usr/lib/jvm/adoptopenjdk-11-hotspot-amd64/bin/java -javaagent:/home/...
{"user":"java","pass":"jpass"}

Process finished with exit code 0

Hi Dan.
Thank you so much. The code worked. Now I will work to improve my example. Thanks