go to post Dan Pasco · Mar 20 Thank you for that information. I researched this and was told that we support Java 8+, not just 8 and 11. We submitted a request to correct the documentation.
go to post Dan Pasco · Mar 19 You should be able to use Java 17 now for your projects. Are you specifically asking for when we will support Java 17 (and perhaps later versions) when using External Java Server/Java Gateway? If that is the case then I believe you can do that in 2024.1.0.
go to post Dan Pasco · Feb 29 I can provide a solution for this but it comes with warnings - SQL doesn't do well with polymorphic embedded objects. If that isn't an issue then consider using a factory pattern. You must define a base class that extends %SerialObject and it must be instantiable. Then, override %OnNew() to dispatch to the concrete class that you wish to instantiate. Perhaps pass in a type argument. That %OnNew() can return an OREF (or a %Status if an error occurs). If you are interested in this model then I can provide a small demo. -Dan
go to post Dan Pasco · Feb 23 Yes, of course. JSON_TABLE in the FROM clause is just another table and can be used as such. If you have wish to use indexes on JSON values stored in columns then you can always define indexes on computed values. That has been possible even before JSON_TABLE. Keep in mind that dynamic data does not always follow expectations as a field in a dynamic object can be a literal, another object or an array of values. I previously posted about ASQ. You can use ASQ to derive a computed value in your class definition.
go to post Dan Pasco · Feb 23 The ISO Standard syntax for JSON_TABLE includes a FORMAT keyword. We are working on a binary compressed format for dynamic arrays and object but it isn't available yet. In addition to the binary format, we hope support other formats in the future. The default FORMAT, as defined by the Standard, is JSON.
go to post Dan Pasco · Jan 24 Perhaps this will help: https://docs.intersystems.com/irislatest/csp/docbook/platforms/DocBook.U...
go to post Dan Pasco · Jan 23 I don't know if this works on Windows systems but for macOS and Linux (I use Ubuntu), I defined a file in my home folder named .iris_init with these contents: :alias pp do ##class(%ASQ.SetUtils).pp($*):alias parse try { do ##class(%ASQ.SetUtils).pp(##class(%ASQ.Parser).parse("$*").toDao()) } catch e { w !,e.AsSQLMessage() }:alias asq try { do ##class(%ASQ.SetUtils).pp($1.apply("$2")) } catch e { w !,e.AsSQLMessage() }:alias find do find^%z($1,$2,$3,$4) This file is automatically loaded each time I establish an IRIS session.
go to post Dan Pasco · Dec 19, 2023 Hi, When you say "purging" do you mean that you are deleting the class definition? Or do you simply mean that you are deleting the data - perhaps by invoking %DeleteExtent or %KillExtent? IRIS has three types of Classes - datatypes (not instantiable), registered (instantiable), and dynamic (not registered but instantiable). Your question seems to involve only registered types. While any data can be serialized/deserialized, we define an interface for doing so - the so-called Storage Interface. Storage Definition is a class member that can be included in just about any class but we only recognize and process it when the class is either a serial or persistent class. A serial class is a class that extends %SerialObject (meaning that %SerialObject is in its primary superclass hierarchy) and a persistent class is a class that extends %Persistent (again, %Persistent is in its primary superclass hierarchy). For these classes, the class compiler will ensure there is a fully defined Storage Definition. As with all class members, the Storage Definition has a type class. This type class is often referred to as the Storage Class - it is a class that implements the Storage Interface. The Storage Definition defines the format of the serialized object. For serial classes, this serialized object value is not directly stored anywhere. Rather, the serialized value is expected to be stored as a value embedded inside of another object. For persistent classes, an object is serialized, perhaps into multiple nodes, and stored in a global at a location defined by the IDKEY index. Persistent classes can also use globals for storing other things such as indexes, counters, metadata and so on. The set of all globals used by a persistent class is referred to as the Extent Set which is, by default, registered with the Extent Manager. When you remove the Storage Definition from a serial or persistent class, then it is possible that any existing data serialized using the removed Storage Definition will not be able to be deserialized as it might not be compatible with a newly defined Storage Definition. We only recommend removing a Storage Definition from a class if there are no serialized/stored instances of the class present. When you delete a persistent class, you can also delete the data and the Extent Set by adding the /deleteextent qualifier (or the 'e' flag) to the command. You can also delete the data by invoking %DeleteExtent (logical delete) or %KillExtent (physical delete - no constraints are enforced). That only deletes the data and leaves the Extent Set registered with the Extent Manager. You can delete the Extent Set from the Extent Registry by using utilities we provide. Start here: https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.... I am happy to help if you wish (I wrote much of the core serialization/deserialization/storage code) but I will need to know more about the types of classes you are using and the issues you have. -Dan
go to post Dan Pasco · Dec 9, 2023 We posted a sample project to the community github account. There are java and dotnet versions of the project. Take a look and we would appreciate any feedback you may want to offer. https://github.com/intersystems-community/samples-dynamicgateway-java https://github.com/intersystems-community/samples-dynamicgateway-dotnet Please note that there are some deficiencies when working with parameterized Java types when using the new External Java Server. Some of these were discovered during the refactoring of the %Net.Remote.Java.Test code. Some have already been addressed but not yet included in a release.
go to post Dan Pasco · Dec 6, 2023 Can you please zwrite sqlResult? It should be an instance of %Library.ProcedureContext. It has properties for sqlcode, message, result set sequence, etc. Knowing what is there will be very helpful.
go to post Dan Pasco · Dec 4, 2023 I came up with this quick and dirty example. I did discover a problem when I executed this using the Database Console in IntelliJ - evidently the IRIS Server requires statement results to be only ResultSets! (I've reported that problem). If the result of executing the insert statements is not added to the %sqlcontext then this works through client connections, otherwise an error is reported. I'll include the result of executing this from the command line and then displaying the results. Also, the results from executing this from a client with the insert results suppressed. DROP TABLE IF EXISTS demo_person; DROP PROCEDURE IF EXISTS multi_results; CREATE TABLE demo_person (name VARCHAR(20), age INT, home_city VARCHAR(40)); CREATE PROCEDURE multi_results() RESULT SETS LANGUAGE OBJECTSCRIPT { set ins = $system.SQL.Prepare("insert into demo_person(name, age, home_city) VALUES(?,?,?)") do $system.SQL.Execute("TRUNCATE TABLE demo_person") do %sqlcontext.AddResultSet(ins.execute("Dan", 25, "Miami")) do %sqlcontext.AddResultSet(ins.execute("Jorge", 32, "Tampa")) do %sqlcontext.AddResultSet(ins.execute("Enrico", 29, "Turin")) do %sqlcontext.AddResultSet(ins.execute("Alexy", 21, "London")) do %sqlcontext.AddResultSet($system.SQL.Execute("SELECT name, age, home_city FROM demo_person ORDER BY age DESC")) }; CALL multi_results(); USER>set result = $system.SQL.Execute("call multi_results()") USER>do result.%Display() Dumping result #1 1 Row Affected Dumping result #2 1 Row Affected Dumping result #3 1 Row Affected Dumping result #4 1 Row Affected Dumping result #5 name age home_city Jorge 32 Tampa Enrico 29 Turin Dan 25 Miami Alexy 21 London 4 Rows(s) AffectedThe results when not adding the insert results as displayed by a database console: name age home_city Jorge 32 Tampa Enrico 29 Turin Dan 25 Miami Alexy 21 London
go to post Dan Pasco · Nov 20, 2023 We are working on a project to post to the InterSystems Corporation Github. The project will include Java, ObjectScript, and DotNet. Stay tuned!
go to post Dan Pasco · Nov 16, 2023 Along with a co-worker, I worked on this for three days. The good news is that I have something to show. The not-so-good news is that we discovered a bug and a deficiency. The better news is that the next version of JDBC to be released will include these fixes and enhancements. Also good news is that most of the old %Net.Remote.Java.Test class works with minor refactoring. Mo Cheng will be adding information on how to leverage a more backward compatible Java Gateway. The legacy Java Gateway used generated classes and the new Dynamic Gateway ($system.external) does not. I'll leave that description for Mo to provide. For now - pending review - I am ready to share a new class (actually a full project). I am just waiting for a place for this to be posted. In the meantime, I could post code snips here. Let me know.
go to post Dan Pasco · Nov 15, 2023 In general, a <LIST> error indicates a malformed $list value. USER>set bad = "something wrong" USER>write $li(bad,1) WRITE $LI(bad,1) ^ <LIST> USER>set good = $listbuild("something right") USER>write $li(good,1) something right In %SerializeObject, this typically is caused by an expectation that a value is a valid list but it is not. In a more complex class that extends %SerialObject, one that contains at least one property whose type class is a swizzlable (persistent, serial, stream), $list(value, n) is used to extract the object value (id or serial value) from a value that is expected to be an OID. While OID's an be more complex, the typical OID format is $list(<serialized object value>, <most specific type class>). <serialized object value> can be a simple ID and <most specific type class> (MSTC) is the class of which the object is an instance but not an instance of any subclass of MSTC. That isn't the only possibility but it is the most likely. I can't be more specific without seeing the full class definition. -Dan
go to post Dan Pasco · Nov 13, 2023 Enrico, For some reason I didn't get an email alert that you had replied. I'm sure it is buried in my "group" mail somehwere - that's my fault and I'll try to get that sorted. %Net.Remote.DotNet.Test - I can't help much with that but I certainly can with %Net.Remote.Java.Test. I'll take a look at that now. $system.external.* is just a helper to get a gateway connection to an external language server such as the Java Server. I refer to the "gateway" as the connection. Maybe that doesn't work for every one. Sorry :( Once you have a gateway connection you can interact with the external Server using methods supported by that connection. Some of those methods are still unknown to me so I'll have to do some research. Stay tuned! Dan
go to post Dan Pasco · Nov 6, 2023 Hi Enrico, I am listening! $system.external does not replace the old Java Gateway but the old Java and JDBC Gateways should still be available. I am not intimately familiar with either of those although I did some of the refactoring work with them both. I am not aware of any user code changes that are necessary to keep using them. If you want to use the new External Java Server then you will necessarily need to write new code. I am happy to provide demos showing how this can be done. I can also assist you with implementing your own project to get you started. Just let me know how you wish to proceed. As for leveraging existing Java libraries, you should be able to do many things without writing new Java code but not everything. If you have some examples of libraries you have attempted to use but were not successful, please provide information so that I can attempt to replicate your issues. As always, I am here, listening, and happy to help!! -Dan
go to post Dan Pasco · Oct 24, 2023 @RobertCemper - our paths have crossed a time or two. I hope they will again.
go to post Dan Pasco · Oct 23, 2023 Hi, Using the new(ish) External Java Server and Gateway connection, this worked for me. I copied your class, refactored it for the Apache PDFBox version 3.0.0 and then called it from the command line. I did receive some warnings that have nothing to do with PDFBox, they are related to the larger project that I placed this code into. The call worked just fine. I don't know exactly when the new external language server/gateway support was added but it is documented in 2021.1: https://docs.intersystems.com/iris20211/csp/docbook/DocBook.UI.Page.cls?KEY=BEXTSERV_intro. I'm pasting the IRIS Session commands I used: USER>set java = $system.external.getJavaGateway() USER>do java.addToPath("<path to my project/target/intersystems-demo-1.0-SNAPSHOT.jar") USER>set text = java.invoke("demo.intersystems.utils.PdfToText","getText","/home/danp/Downloads/DM32.2-2017-00157-ambiguity-in-JSON-array-constructor.pdf") USER>zw text text="ISO/IEC JTC1/SC32 WG3:CMH-023"_$c(10)_"ANSI INCITS DM32.2-2017-00157"_$c(10)_"- 1 of 7 -"_$c(10)_"Title: Ambiguity in <JSON array constructor>"_$c(10)_"Author: <<snipped>> I even have a nice way to return the text as an instance of %Stream.Object if that is interesting to you. HTH, Dan
go to post Dan Pasco · Sep 13, 2023 I don't recommend using the "embedded extent" model. It might be possible to achieve this model but it isn't simple to do and it does produce some odd behavior. I could, reluctantly, describe how this is done. It may not apply to this case anyway. The default serialization of a LIST collection is as an embedded list and we do not support - currently - a serialized list as a child table. This is an example of a MANY to MANY relationship. IRIS does not support this relationship type. Rather, we recommend using the Associative Entity model where there is a third class/table that maintains the relationship. The structure of the AE class/table is quite simple, consisting of two properties/columns, one referencing of the related classes and the other referencing the other related classes. If restructuring the user class is not possible then the %OnDelete recommendation is good. Perhaps it would be better to use a FOREACH=ROW/OBJECT, ONAFTER=DELETE trigger to iterate over the collection and delete each referenced item. Triggers have the added advantage of working with both Object and SQL filing operations. Keep in mind that collections use OID format when crafting the delete.
go to post Dan Pasco · Sep 13, 2023 Interesting question. There is no good option to manipulate the ObjectRegistry but you do have options to access stored data. My recommended option you've already taken off the table. The FOREACH=ROW/OBJECT trigger is by far the best and easiest solution if you require access to the version of an object that is currently stored. There are several reasons why this is the best option, perhaps the most important being that ROW/OBJECT triggers are consistently applied between Objects and SQL. Of course, the same restrictions exist for triggers that exist for %OnBeforeSave - we don't recommend modifying objects/rows. Another option is to use <property>GetStored. This may not work for every situation but it does allow code to retrieve the value of a property directly from storage. I believe this is restricted to what we call "default storage". That isn't an entirely true statement but it is for common storage types (SQL Mapped storage being the other common type). USER>zw ^demo.person.1(1) ^demo.person.1(1)=$lb("Doe, John","123 Main Street","Cambridge","MA","02142","[{""type"":""mobile"",""country_code"":""1"",""number"":""999-999-9999""},{""type"":""work"",""country_code"":""1"",""number"":""888-888-8888""}]") USER>set person = ##class(demo.intersystems.Person).%OpenId(1) USER>write person.name Doe, John USER>set person.name = "Richard, Maurice" USER>write person.nameGetStored(person.%Id()) Doe, John USER>write person.name Richard, Maurice Of course, you can always use SQL to retrieve stored values. For the intrepid explorer, there is another option. This code isn't supported as the %Load* api's aren't supposed to be public. I'm sure most people have examined generated code and have discovered these methods so I'm divulging any big secrets here. First, the results and then the code. USER>set person = ##class(demo.intersystems.Person).%OpenId(1) USER>set person.name = "Enono, William" USER>set sperson = ##class(demo.intersystems.Person).GetStored(person.%Id()) USER>write sperson.name Leavitt, Timothy USER>write person.name Enono, William Fair warning - this code isn't supported, it is considered to be user-implemented code. I also didn't rigorously test this code. I added this class method to my demo.intersystems.Person class: ClassMethod GetStored(id As %Integer) As demo.intersystems.Person { try { set obj = ..%New() set cur = obj.%Concurrency set obj.%Concurrency = 0 $$$THROWONERROR(sc,obj.%LoadInit(,,1)) $$$THROWONERROR(sc, obj.%LoadData(id)) set obj.%Concurrency = cur do obj.%SetModified(0) } catch e { set obj = $$$NULLOREF } return obj } The object referenced by the oref returned by this code is not assigned an id. If you were to save this object it would create a new stored object and it would be assigned its own ID. Exercise caution.