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
Comments
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 EDTI 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:
ClassUtils.CSW1JavaFunctions{ ClassMethodIrisReturn(user = "user", pass = "pass") As%Stream.GlobalBinary { try { setcswStream=##class(%Stream.GlobalBinary).%New() setcswReturn = {"user":(user), "pass":(pass) } docswReturn.%ToJSON(cswStream) returncswStream } catchexc { 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