Search

Clear filter
Article
Eduard Lebedyuk · Mar 24, 2017

Logging using macros in InterSystems IRIS

In my previous article, we reviewed possible use-cases for macros, so let’s now proceed to a more comprehensive example of macros usability. In this article we will design and build a logging system. Logging system Logging system is a useful tool for monitoring the work of an application that saves a lot of time during debugging and monitoring. Our system would consist of two parts: Storage class (for log records) Set of macros that automatically add a new record to the log Storage class Let’s create a table of what we need to store and specify when this data can be obtained – during compilation or at runtime. This will be required when working on the second part of the system - macros, where we will aim to have as many loggable details during compilation as possible: Information Obtained during Event type Compilation Class name Compilation Method name Compilation Arguments passed to a method Compilation Line number in the cls source code Runtime Line number in the generated int code Runtime Username Runtime Date/Time Runtime Message Runtime IP address Runtime Let’s create an App.Log class containing the properties from the table above. When an App.Log object is created, User Name, Date/Time and IP address properties are filled out automatically. App.Log class: Class App.Log Extends %Persistent { /// Type of event Property EventType As %String(MAXLEN = 10, VALUELIST = ",NONE,FATAL,ERROR,WARN,INFO,STAT,DEBUG,RAW") [ InitialExpression = "INFO" ]; /// Name of class, where event happened Property ClassName As %Dictionary.Classname(MAXLEN = 256); /// Name of method, where event happened Property MethodName As %String(MAXLEN = 128); /// Line of int code Property Source As %String(MAXLEN = 2000); /// Line of cls code Property SourceCLS As %String(MAXLEN = 2000); /// Cache user Property UserName As %String(MAXLEN = 128) [ InitialExpression = {$username} ]; /// Arguments' values passed to method Property Arguments As %String(MAXLEN = 32000, TRUNCATE = 1); /// Date and time Property TimeStamp As %TimeStamp [ InitialExpression = {$zdt($h, 3, 1)} ]; /// User message Property Message As %String(MAXLEN = 32000, TRUNCATE = 1); /// User IP address Property ClientIPAddress As %String(MAXLEN = 32) [ InitialExpression = {..GetClientAddress()} ]; /// Determine user IP address ClassMethod GetClientAddress() { // %CSP.Session source is preferable #dim %request As %CSP.Request If ($d(%request)) { Return %request.CgiEnvs("REMOTE_ADDR") } Return $system.Process.ClientIPAddress() } } Logging macros Usually, macros are stored in separate *.inc files containing their definitions. The necessary files can be included into classes using the Include MacroFileName command, which in this case will look as follows: Include App.LogMacro. To start, let’s define the main macro that the user will add to their application’s code: #define LogEvent(%type, %message) Do ##class(App.Log).AddRecord($$$CurrentClass, $$$CurrentMethod, $$$StackPlace, %type, $$$MethodArguments, %message) This macro accepts two input arguments: Event Type and Message. The Message argument is defined by the user, but the Event Type parameter will require additional macros with different names that will automatically identify the event type: #define LogNone(%message) $$$LogEvent("NONE", %message) #define LogError(%message) $$$LogEvent("ERROR", %message) #define LogFatal(%message) $$$LogEvent("FATAL", %message) #define LogWarn(%message) $$$LogEvent("WARN", %message) #define LogInfo(%message) $$$LogEvent("INFO", %message) #define LogStat(%message) $$$LogEvent("STAT", %message) #define LogDebug(%message) $$$LogEvent("DEBUG", %message) #define LogRaw(%message) $$$LogEvent("RAW", %message) Therefore, in order to perform logging, the user only needs to place the $$$LogError("Additional message") macro in the application code.All we need to do now is to define the $$$CurrentClass, $$$CurrentMethod, $$$StackPlace, $$$MethodArguments macros. Let’s start with the first three: #define CurrentClass ##Expression($$$quote(%classname)) #define CurrentMethod ##Expression($$$quote(%methodname)) #define StackPlace $st($st(-1),"PLACE") %classname, %methodname variables are described in the documentation. The $stack function returns INT code line number. To convert it into CLS line number we can use this code. Let's use the %Dictionary package to get a list of method arguments and their values. It contains all the information about the classes, including method descriptions. We are particularly interested in the %Dictionary.CompiledMethod class and its FormalSpecParsed property, which is a list: $lb($lb("Name","Classs","Type(Output/ByRef)","Default value "),...) corresponding to the method signature. For example: ClassMethod Test(a As %Integer = 1, ByRef b = 2, Output c) will have the following FormalSpecParsed value: $lb( $lb("a","%Library.Integer","","1"), $lb("b","%Library.String","&","2"), $lb("c","%Library.String","*","")) We need to make $$$MethodArguments macro expand into the following code (for the Test method): "a="_$g(a,"Null")_"; b="_$g(b,"Null")_"; c="_$g(c,"Null")_";" To achieve this, we have to do the following during compilation: Get a class name and a method name Open a corresponding instance of the %Dictionary.CompiledMethod class and get its FormalSpec property Convert it into a source code line Let's add corresponding methods to the App.Log class: ClassMethod GetMethodArguments(ClassName As %String, MethodName As %String) As %String { Set list = ..GetMethodArgumentsList(ClassName,MethodName) Set string = ..ArgumentsListToString(list) Return string } ClassMethod GetMethodArgumentsList(ClassName As %String, MethodName As %String) As %List { Set result = "" Set def = ##class(%Dictionary.CompiledMethod).%OpenId(ClassName _ "||" _ MethodName) If ($IsObject(def)) { Set result = def.FormalSpecParsed } Return result } ClassMethod ArgumentsListToString(List As %List) As %String { Set result = "" For i=1:1:$ll(List) { Set result = result _ $$$quote($s(i>1=0:"",1:"; ") _ $lg($lg(List,i))_"=") _ "_$g(" _ $lg($lg(List,i)) _ ","_$$$quote(..#Null)_")_" _$s(i=$ll(List)=0:"",1:$$$quote(";")) } Return result } Let’s now define the $$$MethodArguments macro as: #define MethodArguments ##Expression(##class(App.Log).GetMethodArguments(%classname,%methodname)) Use case Next, let's create an App.Use class with a Test method to demonstrate the capabilities of the logging system: Include App.LogMacro Class App.Use [ CompileAfter = App.Log ] { /// Do ##class(App.Use).Test() ClassMethod Test(a As %Integer = 1, ByRef b = 2) { $$$LogWarn("Text") } } As a result, the $$$LogWarn("Text") macro in the int code converts into the following line: Do ##class(App.Log).AddRecord("App.Use","Test",$st($st(-1),"PLACE"),"WARN","a="_$g(a,"Null")_"; b="_$g(b,"Null")_";", "Text") Execution of this code will create a new App.Log record: Improvements Having created a logging system, here's some improvement ideas: First of all, a possibility to process object-type arguments since our current implementation only logs object oref. Second, a call to restore the context of a method from stored argument values. Processing of object-type arguments The line that puts an argument value to the log is generated in the ArgumentsListToString method and looks like this: "_$g(" _ $lg($lg(List,i)) _ ","_$$$quote(..#Null)_")_" Let's do some refactoring and move it into a separate GetArgumentValue method that will accept a variable name and class (all of which we know from FormalSpecParsed) and output a code that will convert the variable into a line. We'll use existing code for data types, and objects will be converted into JSON with the help of SerializeObject (for calling from the user code) and WriteJSONFromObject (for converting an object into JSON) methods: ClassMethod GetArgumentValue(Name As %String, ClassName As %Dictionary.CacheClassname) As %String { If $ClassMethod(ClassName, "%Extends", "%RegisteredObject") { // it's an object Return "_##class(App.Log).SerializeObject("_Name _ ")_" } Else { // it's a datatype Return "_$g(" _ Name _ ","_$$$quote(..#Null)_")_" } } ClassMethod SerializeObject(Object) As %String { Return:'$IsObject(Object) Object Return ..WriteJSONFromObject(Object) } ClassMethod WriteJSONFromObject(Object) As %String [ ProcedureBlock = 0 ] { Set OldIORedirected = ##class(%Device).ReDirectIO() Set OldMnemonic = ##class(%Device).GetMnemonicRoutine() Set OldIO = $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) Do ##class(%ZEN.Auxiliary.jsonProvider).%ObjectToJSON(Object) } Catch Ex { Set Str = "" } //Return to original redirection/mnemonic routine settings If (OldMnemonic '= "") { Use OldIO::("^"_OldMnemonic) } Else { Use OldIO } Do ##class(%Device).ReDirectIO(OldIORedirected) 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 } A log entry with an object-type argument looks like this: Restoring the context The idea of this method is to make all arguments available in the current context (mostly in the terminal, for debugging). To this end, we can use the ProcedureBlock method parameter. When set to 0, all variables declared within such a method will remain available upon quitting the method. Our method will open an object of the App.Log class and deserialize the Arguments property. ClassMethod LoadContext(Id) As %Status [ ProcedureBlock = 0 ] { Return:'..%ExistsId(Id) $$$OK Set Obj = ..%OpenId(Id) Set Arguments = Obj.Arguments Set List = ..GetMethodArgumentsList(Obj.ClassName,Obj.MethodName) For i=1:1:$Length(Arguments,";")-1 { Set Argument = $Piece(Arguments,";",i) Set @$lg($lg(List,i)) = ..DeserializeObject($Piece(Argument,"=",2),$lg($lg(List,i),2)) } Kill Obj,Arguments,Argument,i,Id,List } ClassMethod DeserializeObject(String, ClassName) As %String { If $ClassMethod(ClassName, "%Extends", "%RegisteredObject") { // it's an object Set st = ##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject(String,,.obj) Return:$$$ISOK(st) obj } Return String } This is how it looks in the terminal: >zw >do ##class(App.Log).LoadContext(2) >zw a=1 b=<OBJECT REFERENCE>[2@%ZEN.proxyObject] >zw b b=<OBJECT REFERENCE>[2@%ZEN.proxyObject] +----------------- general information --------------- | oref value: 2 | class name: %ZEN.proxyObject | reference count: 2 +----------------- attribute values ------------------ | %changed = 1 | %data("prop1") = 123 | %data("prop2") = "abc" | %index = "" What’s next? The key potential improvement is to add another argument to the log class with an arbitrary list of variables created inside the method. Conclusions Macros can be quite useful for application development. Questions Is there a way to obtain line number during compilation? Links Part I. Macros GitHub repository Cool article Edward! Nevertheless I have a little note here. According to my experience working on a huge code base, logging with macros may turn to a big pain as soon as you decide to change (add, etc) arguments to your macro, or in other words, change the macro code. We had an application full of logging macros with something like you mentioned: #define LogFatal(%message) $$$LogEvent("FATAL", %message) And at some point the logging mechanism became working improperly and we needed to change it (yuh, this logger used TCP connections and some other stuff to behave like "universal" logger in the application). But simply changing the macros code was not the case: we needed to recompile everything (as macros is compile-time code generator)! Every class on live system which used this macro. This was not successful (even on test environment) for the reasons I don't remember to be honest. What we did instead is... We spent a few days replacing all the macro code to simple class method calls like do ##class(Logger.Log).Warn(whatever). With this approach, when you change the Warn method and recompile, everything is applied immediately. The pros I see for using macros to class methods is the thing that you can type around 15 less characters for logging, and optionally have some compile-time expressions about the logged class using macros. But the cons here is using macros itself... I believe the similar approach can be done with class methods and with the help of $stack variable. Any comments on this? Thanks! Nice article Eduard. Re: last comment. Not sure how having to change the implementation of a Macro is any less of a pain than having to change hundreds of class method arguments. If anything Macro's are a great way to abstract application wide changes with a single line of code. Granted recompilation is required, but code that can't withstand recompilation all day long has a deeper problem. My only negative about Macro's is when you can't read the code for Macro soup, less is more for me. One tip to add. I like to have auto complete work for Macros. If you precede each macro with a triple comment, then they will appear in the auto complete suggestions... /// #define LogNone(%message) $$$LogEvent("NONE", %message) /// #define LogError(%message) $$$LogEvent("ERROR", %message) /// #define LogFatal(%message) $$$LogEvent("FATAL", %message) I think the issue of compilation order specification is more at fault here. Full compile from scratch (on a new instance) or a full recompile should work without errors every time. If it's not, set DependsOn keyword strategically. Or System. 💡 This article is considered as InterSystems Data Platform Best Practice. Hi I have developed a Debug Logging system that is not quite as expansive as this version. The Debug Log class has the following properties: Classname Username CreatedTS Key Message It is called with one macro #define DebugLog(%s1,%s2,%s3,%s4) where %s1 = Username (defaults to $username) %s2 is the Key which could be a method name or some other meaningful way to locate logs from a certain area in your code %s3 is the Message which is a string of Human readable text %s4 is a %Status returned by reference (in case the code has crashed) I use it in all of my developments There is a Configuration class as well that basically determines whether logging is turned on or off depending on whether you are working in DEV or PRODUCTION. The class comes with a number of methods including a Purge method that will purge all logs older than a certain number of days. If anyone is interested in this please let me know Nigel
Question
Laura Cavanaugh · Aug 1, 2017

How to change the Ensemble by InterSystems logo?

My boss would like to change the Ensemble logo that one sees in the mangement portal, because it's part of the DeepSee Anaylzer. I can see where it lives on the generated html. I kow that you can set the logo for the User POrtal settings in DeepSee -- you can specifiy a URL for your logo. But we'd like to go one stee further and change the Ensemble by InterSystems to our own logo / company name. Is it possible to change this in the code? Is there a Configuration setting to change this?Thanks,Laura Is that uhmm... an empty rectangle? For me there is the InterSystems Ensemble Logo - NOW Yeah, you can't upload photos directly to this forum and it is somewhat bad, even though there's a lot of image related configs that most won't even think about using... The only way is hosting in the cloud. Yep, now it's displaying for me too. You can upload images using this button: Thanks Ed! I just went through old DC post for half an hour+ to detect it. It's much better than my link to Facebook. Regards I can't believe how much I look forward to these discussions. Unfortunately, there is no EnsembleLogo* anywhere in the InterSystems directory. It almost looks like it's simple text, made to look like a logo using CSS. This is from the page source: <div class="portalLogoBox"> <div class="portalLogo" title="Powered by Zen">Ensemble</div> <div class="portalLogoSub">by InterSystems</div> The class portalLogoBox must do all that CSS/html stuff to make it look cool. I was wondering if I can change the text from Ensemble to something else. If the user is in the DeepSee Analyzer, it will say DeepSee by InterSystems instead, but with similar CSS modifications.I figured out why he wants to do this; we have a few "portal" users who have accss to all of the clients' namespaces. For a demo, they will go into the DeepSee UserPortal, then exit to the management portal to switch namespaces, then go back in to the DeepSee user portal. THe DeepSee user portal has settings where you can change the logo that is displayed (what are those classes? I need to be able to change them programmatically rather than manually 50 times!) but when the portal users go back to the management portal, our company logo is lost; instead the lovely Ensemble by InterSystems (Powered by Zen) is there. I personally think IS should get the credit, but my boss is wondering if we can change it for the purposes of these demos. If not, that's OK; but now I'm simply curious. Thanks!Laura I could swear it was related to this widget's toolbar. It seems they have changed it to an image for newer releases. (Even Caché is now an image instead of pure CSS).Well, I do agree with crediting IS, however it should be something like:"Powered by InterSystems ENSEMBLE" like a single message, since the focus is to demonstrate your work, not theirs. do you look for this one?%ENSInstallDir%\CSP\broker\portal\EnsembleLogo210x50.png I often forget to mention that we are on 2014. Thanks - at least I can tell him we can't do it but maybe in a future release we can change the IS logo.Thanks,Laura
Announcement
Evgeny Shvarov · Aug 2, 2017

InterSystems Developer Meetup: Who are the Presenters?

Hi, Community! You know we would have a meetup in Boston on the 8th of July August with the following agenda: Time Topic Presenter 5-30pm Registration and welcome coffee 5-55pm Opening Evgeny Shvarov 6-00pm Atelier 1.1 Andreas Dieckow, Joyce Zhang, Michelle Stolwyk 6-30pm REST API in Caché Fabian Haupt 7-00pm Coffee break 7-30pm Online Learning and Developer Community Douglas Foster, Evgeny Shvarov 8-00pm End of the Meetup. Coffee, beverages Let me share some details about presenters. Andreas Dieckow the product manager, Joyce Zhang the tech lead and Michelle Stolwyk the UX designer from the Atelier team will be sharing the benefits of the InterSystems new IDE and introducing the new features in the next release. Fabian Haupt is an InterSystems senior support engineer, you might have worked with him on one of your WRC issues. Fabian is also an honored author on Developer Community. He will share some best practices on implementing REST API backends using InterSystems Caché, Ensemble, HealthShare. Douglas Foster, manager of the InterSystems Online Learning department will present the latest news about online learning and how the courses have evolved. He will demonstrate the resources available now and will gather feedback about what to include in the future online learning catalog. And you can introduce your topic on the next meetup and share your success built with InterSystems Technology. Join us on the 8th of August! You forgot Evgeny Shvarov, community manager of InterSystems who will persuade you to join and contribute to our beloved Developers Community! 8th of July? Thank you, Sergey! Will do! ) 8th of August of course! Thanks) So I've used my moderator superpowers to correct the first line of the post. Thanks, John!
Announcement
Evgeny Shvarov · Jun 9, 2017

Meet InterSystems Developer Community Telegram!

Hi, Community!We are introducing Telegram Channel for Developer Community most interesting articles, discussions, events and announcements! If you already use this messenger, join the DC Channel so you can easily follow what's new on Developer Community.See how it looks:Join! The permalink to join Telegram can be found also on the right of every DC page: And... may I ask you introduce your ideas on Developer Community Telegram bot?Please share it below? Thank you in advance! Why do I have to join Telegram to see the data on Atelier?I thought joining the Community was enough? HI, Mike!You don't have to. DC Telegram Channel is introduced for those who loves Telegram mobile app as a way to communicate.It is yet another channel to deliver DC content which is already posted on DC or DC YouTube.But, we plan to introduce DC bot which can be interesting and unique for DC Telegram Channel.
Article
Eduard Lebedyuk · Apr 3, 2017

Firefox search engine for InterSystems documentation

If you're using Firefox, you probably know that there are search engine addons that allow us to use website search directly. Now, docs.intersystems.com is also available as a search engine here. Here's how it looks:And after clicking on InterSystems search engine you're redirected to the docs.intersystems.com:Download here.UPD. Added InterSystems IRIS documentation. Download. Very neat! Would be much better, if InterSystems will add OpenSearch to their documentation. And I think it will be enough only for the public version. And in this case, it will be supported in all modern browsers, without installation anything external. Something like this. <?xml version="1.0"?> <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> <ShortName>InterSystems docs latest</ShortName> <Description>InterSystems Ensemble Documentation latest Version</Description> <Url type="text/html" method="get" template="https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.SearchPageZen.cls?KEY=&KeyWord={searchTerms}"/> </OpenSearchDescription> Well, that's actually exactly what this plugin is. <?xml version="1.0" encoding="UTF-8"?> <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> <ShortName>InterSystems</ShortName> <Description>Latest InterSystems documentation search</Description> <InputEncoding>UTF-8</InputEncoding> <Image width="16" height="16">data:image/png;base64,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 </Image> <Url type="text/html" method="GET" template="http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.SearchPageZen.cls" rel="searchform"> <Param name="KeyWord" value="{searchTerms}"/> </Url> </OpenSearchDescription> Added InterSystems IRIS documentation search engine. Download. very comfortable as I always use FFX for searching Hi, Ed!Is it possible to introduce a similar feature for Chrome users too? Sure.Open ChromeGo to Settings | Manage search engines...Scroll to the bottom of the windowIn Add a new search engine, enter InterSystemsFor Keyword, enter iFor URL, enter one of:http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.SearchPageZen.cls?KeyWord=%s - for Ensemble docshttp://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.SearchPageZen.cls?KeyWord=%s - for InterSystems Iris docsClick DoneAfter that you'll be able to search InterSystems documentation in Chrome address bar by typing: i keyword and pressing Enter. Cool!That works, thank you!
Article
Sergey Mikhailenko · Jun 2, 2020

Increasing the Security of the InterSystems IRIS DBMS

When you first start working with InterSystems IRIS, it’s a common practice to install a system with only a minimum level of security. You have to enter passwords fewer times and this makes it easier to work with development services and web applications when you're first getting acquainted. And, sometimes, minimal security is more convenient for deploying a developed project or solution. And yet there comes a moment when you need to move your project out of development, into an Internet environment that’s very likely hostile, and it needs to be tested with the maximum security settings (that is, completely locked down) before being deployed to production. And that’s what we’ll discuss in this article. For more complete coverage of DBMS security issues in InterSystems Caché, Ensemble, and IRIS, you may want to read my other article, [Recommendations on installing the InterSystems Caché DBMS for a production environment.](https://community.intersystems.com/post/recommendations-installing-intersystems-cach%C3%A9-dbms-production-environment) The security system in InterSystems IRIS is based on the concept of applying different security settings for different categories: users, roles, services, resources, privileges, and applications. ![](https://lh6.googleusercontent.com/FoNWn41ToKOKB_gj2rGOpJdSMDN9nWMh0pgjOog1KX8oLf2JntVeQ85t2FETTkT2vYmYSOCXSMi4OXcWE5kzBmEwrWYGIUQxlwr5crjJ_XplyTu2yg2m2ZgU54Skc2n2gi59pSE3) Users can be assigned roles. Users and roles can have privileges on resources — databases, services, and applications — with varying read, write, and use rights. Users and roles can also have SQL privileges on the SQL tables located in databases. # How Security Levels Differ When installing InterSystems IRIS, you can choose the security level: Minimal, Normal, or Locked Down. The levels differ in the degree of user engagement, the available roles and services, and in the configuration of authentication methods for services and applications. For more information, read the [Preparing for InterSystems Security](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GCI_prepare_install#GCI_security) section of the Preparing to Install InterSystems IRIS guide. In the documentation you’ll find the tables shown below, which show the security settings for each level. You can change the settings in the system management portal interface. ## Initial User Security Settings |Security Setting | Minimal | Normal | Locked Down | |--------------------------------------------|--------------|--------------|----------------------| |Password Pattern |3.32ANP |3.32ANP |8.32ANP | |Inactive Limit |0 |90 days |90 days | |Enable _SYSTEM User |Yes |Yes |No | |Roles assigned to UnknownUser |%All |None |None | ## Initial Service Properties |Service Property |Minimal|Normal|Locked Down| |-----------------------------------|-----------|----------|------------------| |Use Permission is Public |Yes |Yes |No | |Requires Authentication |No |Yes |Yes | |Enabled Services |Most |Some |Fewest | ## Initial Enabled Settings for Services |Service | Minimal | Normal | Locked Down | |-----------------------------------|--------------|-------------|---------------------| |%Service_Bindings | Enabled | Enabled | Disabled | |*%Service_CSP | Enabled | Enabled | Enabled | |%Service_CacheDirect | Enabled | Disabled | Disabled | |%Service_CallIn | Enabled | Disabled | Disabled | |%Service_ComPort | Disabled | Disabled | Disabled | |%Service_Console | Enabled | Enabled | Enabled | |%Service_ECP | Disabled | Disabled | Disabled | |%Service_MSMActivate | Disabled | Disabled | Disabled | |%Service_Monitor | Disabled | Disabled | Disabled | |%Service_Shadow | Disabled | Disabled | Disabled | |%Service_Telnet | Disabled | Disabled | Disabled | |%Service_Terminal | Enabled | Enabled | Enabled | |%Service_WebLink | Disabled | Disabled | Disabled | *For InterSystems IRIS, %Service_CSP applies %Service_WebGateway. The services used are slightly different for different operating systems. # How You Can Improve Security For each enabled service, you need to choose the appropriate authentication methods: unauthenticated, password, Kerberos, or delegated. You also need to disable web applications that aren’t used in the system. And for web applications that are enabled, you need to select the correct authentication method: authenticated, password, Kerberos, delegated, login, or cookie. Of course, the administrator chooses the security settings for each project and solution so the project can function according to the customer's requirements. And this is always a balance between keeping the system convenient enough that users can actually get their work done, while also secure enough to keep intruders at bay. As you know, the most secure system is a disabled system. If you encounter a need to manually increase the security level of your system more than once, this is a sure sign you need to write a software module to solve these problems. In fact, InterSystems Open Exchange has a lockdown program that can help you improve security. You’ll find the source code for the program in the repository on the InterSystems [isc-apptools-lockdown](https://openexchange.intersystems.com/package/isc-apptools-lockdown) page. Here’s what the LockDown program does. ## First, it changes passwords for preinstalled users: - Admin, - CSPSystem, - IAM, - SuperUser, - UnknownUser, - _Ensemble, - _SYSTEM. ## Second, it disables all services except: - %%service_web gateway - %service_console - %service_login - %service_terminal ## Next, it sets password protection for all web applications, including: - /csp/ensdemo - /csp/samples - /csp/user - /isc/studio/usertemplates - /csp/docbook - /csp/documatic - /isc/studio/rules - /isc/studio/templates ## Finally, it sets system-wide security parameters including: - Password complexity "8.32 ANP" - Limit on user inactivity of 90 days - Audit and all security-relevant events You can install the LockDown program on your system by downloading [LockDown.cls](https://github.com/SergeyMi37/isc-apptools-lockdown/blob/master/src/cls/App/Security/LockDown.cls) from GitHub. Then, in terminal mode, enter the following: ``` USER>zn “%SYS” %SYS>do $system.OBJ.Load("/home/irisusr/LockDown.cls","ck") ``` Or you can install it using the ZPM batch manager from the public register with the following commands: ``` USER>zn “%SYS” %SYS> zpm “install isc-apptools-lockdown” ``` # Performing a Lockdown Before executing a lockdown, it’s strongly recommended that you perform a backup. The LockDown program must be executed from the %SYS area. If you don't want to change the password for all preinstalled users, leave the first parameter empty. If you want to keep the ability to edit programs and classes using IRIS Studio, Atelier, or VSCode, don’t disable the %Service_Bindings service. To ensure this works, the bindings argument must be set to 1. Here’s an example: `do ##class(App.Security.LockDown).Apply("New Password 123",.msg,1)` This module also contains a function that’s useful if the system password is compromised and you need a replacement for all preinstalled accounts without performing a lockdown. You can run it as follows: `do ##class(App.Security.LockDown).Change Password("New Password 123", "Admin,CSPSystem,IAM,SuperUser,Unknown User, _Ensemble,_SYSTEM")` Most likely, after performing the lockdown, your application or project will stop working. To fix it, you’ll need to restore some security settings to their original state. This can be done either via the management portal interface (security section) or programmatically. # Changing Your Security Settings After Lockdown After lockdown, if your web applications used authentication methods other than passwords, you’ll need to enable them. I suggest running the software module [zpm-registry-test-deployment](https://github.com/intersystems-community/zpm-registry-test-deployment/blob/master/Installer.cls), which has an example of using LockDown for the ZPM-registry project. The code that follows is applied at the end of the installation. The project was installed on IRIS with a minimal level of security. Here’s what the code had to do: - Change passwords for all preinstalled users. - Disable all services not used by this project. - Enable password protection for all applications on the system, except web applications /registry (which allows unauthorized users to get a list of packages in the registry). - Create a new user with privileges to publish new packages in the registry. This user must have write rights to the project tables in the IRISAPP database. Create a new user: ``` set tSC= ##class(App.Security.LockDown).CreateUser(pUsername, "%DB_"_Namespace, pPassword, "ZMP registry user",Namespace) If $$$ISERR(tSC) quit tSC write !,"Create user "_pUsername ``` Add privileges for a new and unauthorized user: ``` set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "1,ZPM.Package", "s", "UnknownUser") set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "1,ZPM.Package", "s", pUsername) set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "1,ZPM.Package_dependencies", "s", pUsername) set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "1,ZPM_Analytics.Event", "s", pUsername) set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "9,ZPM.Package_Extent", "e", pUsername) set tSC=##class(App.Security.LockDown).addSQLPrivilege(Namespace, "9,ZPM_Analytics.Event_Extent", "e", pUsername) If $$$ISERR(tSC) quit tSC write !,"Add privileges " ``` Run the LockDown program: ``` set tSC= ##class(App.Security.LockDown).Apply(NewPassSys) If $$$ISERR(tSC) quit tSC Change the settings for the web app so that an unknown user can log in: set prop("AutheEnabled")=96 set tSC=##class(Security.Applications).Modify("/registry",.prop) If $$$ISERR(tSC) quit tSC write !,"Modify /registry " Change the settings for the %service_terminal service, changing the authorization method to Operating System, Password: set name="%service_terminal" set prop("Enabled")=1 set prop("AutheEnabled")=48 ; Operating System,Password set tSC=##class(Security.Services).Modify(name,.prop) If $$$ISERR(tSC) quit tSC write !,"Modify service terminal" ``` # Wrapping Up In this article, I discussed why you might want to increase the security level of your system and how you’d do this programmatically, and I showed an example using the InterSystems LockDown program. We used a method in which we first closed down everything in the system (that is, we set the maximum security level). We then moderated the security by opening the services and applications necessary for the project to function, but only those. I'm sure there are other ways and best practices, and I’d love to hear about them as part of the discussion of this article by the community. Have you thought about the OS security issues for the instance? For example: if you do a minimal security install on Linux, many more processes run as root than if you do any other install. The only safe way to fix that is via a re-install. I'm always nervous about the idea of converting a minimal install to a more secure one because of those kinds of issues and I don't want people to think their instances are more secure than they really are. Hi Katherine.I agree with you, and the problem of minimizing processes launched from root, my module will not solve
Question
Kishan Ravindran · May 10, 2017

Examples of using FHIR With InterSystems Caché

Is there any simple code in Cache using FHIR to understand the relationship between them? If so can anyone provide some example. Hi KishanI think it would help to have a little bit more information on your particular use case for FHIR. FHIR is the latest standard to be developed under the HL7 organization. Pronounced 'Fire' , FHIR stands for Fast Healthcare Interoperability Resources. FHIR is a standard for exchanging healthcare information electronically. The FHIR standard is just that, a standard, and as is the case for all standards, requires implementation. Complete details about the FHIR standard are publically available on the internet at https://www.hl7.org/fhir/There is no specific functionality built into the InterSystems Cache product to support the FHIR standard, although InterSystems Cache could be used to develop an implementation of the FHIR standard. Such an implementation, done with Cache, would require a significant amount of developmentOn the other hand InterSystems HealthShare does have specific libraries included that can make working with the FHIR standard much easier, but this obviously would depend upon what your exact use-case for FHIR was. If you could provide additional information as to what you are wanting to do with FHIR it would make answering your question much easier.
Question
Manoj Krishnamoorthy · Jul 19, 2017

How To Join InterSystems Global Masters

Hi,I'm interested in participating InterSystems Global Masters!Can anyone send the invitation to join InterSystems Global Masters!Thanks Thanks Evgeny How do I invite a colleague to Global Masters ?Will be able to earn points by sharing the link ? Hi, Manoj!You are invited!Check the mail.
Announcement
Andreas Dieckow · Jan 5, 2018

InterSystems Response to Meltdown and Spectre Vulnerabilities

InterSystems continuously monitors our systems for any evidence of attempts to exploit vulnerabilities such as the newly announced Meltdown and Spectre attack vectors. At this time we have seen no indications of attempts to target InterSystems systems or technology using these vulnerabilities. · InterSystems is aware of recently reported cybersecurity vulnerabilities known as Meltdown and Spectre that affect a wide range of computer processors (See US-CERT Alert TA 18-004A, Meltdown and Spectre Side-Channel Vulnerability Guidance, https://www.us-cert.gov/ncas/alerts/TA18-004A).· As is the case with other technology companies, InterSystems is examining our products to assess and understand the direct impact of the vulnerability and what appropriate actions to take to remediate any discovered risk.· Same as with many other companies, InterSystems’ corporate operations and infrastructure uses systems that are impacted by these vulnerabilities.· InterSystems is working to remediate these operational vulnerabilities as quickly as possible using the applicable patches and firmware updates as they are made available by our vendors.· InterSystems is also monitoring any performance impact caused by patches and firmware update to develop a plan of action to address if necessary.· InterSystems continues to monitor this issue and will provide updates as appropriate.
Announcement
Evgeny Shvarov · Sep 25, 2018

InterSystems IRIS on Microsoft Azure Marketplace

Hi Community! I'm pleased to announce that InterSystems IRIS is available on Microsoft Azure Marketplace! → InterSystems IRIS on Azure Marketplace This is the second public cloud offering of InterSystems IRIS after recently announced Google Cloud Platform support. What's inside? You can find InterSystems IRIS 2018.1.2 for Ubuntu there with BYOL option for the license. Stay tuned!
Article
Gevorg Arutiunian · Jul 6, 2018

GraphQL for InterSystems Data Platforms

![](https://pp.userapi.com/c849024/v849024561/cea1/ayx4tJnAKRE.jpg) [GraphQL](http://graphql.org/) is a standard for declaring data structures and methods of data access that serves as a middleware layer between the client and the server. If you’ve never heard about GraphQL, here is a couple of useful online resources: [here](https://medium.freecodecamp.org/so-whats-this-graphql-thing-i-keep-hearing-about-baf4d36c20cf), [here](https://blog.apollographql.com/graphql-vs-rest-5d425123e34b) and [here](https://blog.apollographql.com/the-anatomy-of-a-graphql-query-6dffa9e9e747). In this article, I will tell you how you can use GraphQL in your projects based on InterSystems technologies. InterSystems platforms currently support several methods of creating client/server applications: - REST - WebSocket - SOAP So what’s the advantage of GraphQL? What benefits does it provide compared with REST, for example? GraphQL has several types of requests: - **query** - server requests for obtaining data, similar to GET requests recommended for fetching data using REST. - **mutation** - this type is responsible for server-side data changes, similar to POST (PUT, DELETE) requests in REST. Both mutation and query can return data – this comes in handy if you want to request updated data from the server immediately after performing a mutation. - **subscriptions** - the same query type that will output data. The only difference is that a query is launched by a page rendered on the client side while subscriptions are activated by mutations. ## Key features and advantages of GraphQL ### It’s up to the client to decide what data should be returned One of the key features of GraphQL is that the structure and volume of returned data are defined by the client application. The client application specifies what data it wants to receive using a declarative, graph-like structure that closely resembles the JSON format. The response structure corresponds to that of the query. Here is how a simple GraphQL query looks: ```json { Sample_Company { Name } } ``` A response in the JSON format: ```json { "data": { "Sample_Company": [ { "Name": "CompuSoft Associates" }, { "Name": "SynerTel Associates" }, { "Name": "RoboGlomerate Media Inc." }, { "Name": "QuantaTron Partners" } ] } } ``` ### Single endpoint When using GraphQL to work with data, we always connect to a single **endpoint**, GQL server, and get different data by changing the structure, fields, and parameters of our queries. REST, in contrast, uses multiple endpoints. Let’s compare REST with GraphQL using a simple example: ![](https://pp.userapi.com/c845124/v845124781/5d6c7/pOFLQsSnzk0.jpg) Let’s assume that we need to load a user’s content. If we are using REST, we need to send three queries to the server: 1. Get the user’s data by their id 2. Use their id to load their posts 3. Use their id to get a list of their followers/subscribers Below is a REST map corresponding to these queries: ``` ``` In order to get a new data set, we will need to update this REST map with a new endpoint. GraphQL handles this with a single query. To do that, just specify the following in the request body: ``` { operationName: null, //a query can have a name ( query TestName(...){...} ) query: "query { User(id: "ertg439frjw") { name posts { title } followers(last: 3) { name } } }", variables: null // initialization of the variables used in the query } ``` A REST map corresponding to this query: ``` ``` Note that this is the only endpoint on the server. ## Installing GraphQL and GraphiQL In order to start using GraphQL, you need to complete a few steps: 1. Download the [latest release](https://github.com/intersystems-ru/GraphQL/releases) from GitHub and import it to the necessary namespace 2. Go to the system management portal and create a new web application based on your InterSystems Data Platform product (Caché, Ensemble or IRIS): - Name - **/** - Namespace - **for example, SAMPLES** - Handler class - **GraphQL.REST.Main** 3. GraphiQL — a shell for testing GraphQL queries. Download the [latest build](https://github.com/intersystems-ru/GraphQL/releases) or [build](https://github.com/graphql/graphiql) from the source on your own. 4. Create a new web application: - Name - **/graphiql** - Namespace - **for example, SAMPLES** - Physical path to CSP files - **C:\InterSystems\GraphiQL\** ## Let’s take a look at the result Go to the following link in your browser **http://localhost:57772/graphiql/index.html** (localhost — server, 57772 — port) ![GraphiQL](https://pp.userapi.com/c848736/v848736070/20e2d/Ca1U73N455w.jpg) I hope everything is clear with the **Query** and **Response** namespaces. A **Schema** is a document that is generated for all stored classes in a namespace. The schema contains: - Classes - Properties, arguments, and their types - Descriptions of all of the above generated from comments Let’s take a closer look at a schema for the **Sample_Company** class: ![](https://habrastorage.org/webt/x6/vs/np/x6vsnpq7seel9ndzdvsn1nyzcys.jpeg) GraphiQL also supports automatic code completion that can be activated by pressing the **Ctrl + Space** key combination: ![](https://habrastorage.org/webt/8h/51/wz/8h51wz5ccdiabmsn06n9sve_mce.jpeg) ## Queries Queries can be simple and complex for several sets of data. Below is a sample query for data from to different classes, **Sample_Person** and **Sample_Company**: ![](https://pp.userapi.com/c845221/v845221237/55384/TS0QwWXpurI.jpg) ## Filtering At the moment, only strict equality is supported: ![filter](https://pp.userapi.com/c845221/v845221849/5045f/Q0kUiLLXXPQ.jpg) ## Pagination Pagination is supported through 4 functions that can be combined to achieve the necessary result: - **after: n** – all records with id greater than n - **before: n** – all records with id smaller than n - **first: n** – first n records - **last: n** – last n records ![pagination](https://pp.userapi.com/c845221/v845221237/553cd/_g6sZTZ5qpA.jpg) ## Visibility areas In most situations, the business logic of an application dictates that particular clients only have access to particular namespace classes (role-based permissions). Based on that, you may need to limit class visibility for a client: - All classes in the namespace (**GraphQL.Scope.All**) - Classes inherited from a superclass (**GraphQL.Scope.Superclass**) - Classes belonging to a particular package (**GraphQL.Scope.Package**) In order to change the method of visibility restriction, open the studio, switch to the necessary namespace, and open the **GraphQL.Settings** class. It has a **SCOPECLASS** parameter with the default value of **GraphQL.Scope.All** — this is the class containing the description of the class visibility interface in the namespace: ![scope](https://pp.userapi.com/c830109/v830109849/fc892/fYFBSE-q2Ws.jpg) To change class visibility restrictions, you need to set one of the values provided above: **GraphQL.Scope.Package** or **GraphQL.Scope.Superclass**. If you picked **GraphQL.Scope.Package**, you will also need to go to that class and change the value of the **Package** parameter to the name of the necessary package – for instance, **Sample**. This will make all the stored classes from this package fully available: ![](https://pp.userapi.com/c830109/v830109849/fc8d9/i2yDROI61Ys.jpg) If you picked **GraphQL.Scope.Superclass**, simply inherit from this class once again in the necessary classes:: ![](https://pp.userapi.com/c830109/v830109437/fbc84/xPpHmCKd0g4.jpg) ## Currently supported Queries: - Basic - Embedded objects - Only many to one relation - List of simple types - List of objects ## Currently under development Queries: - Embedded objects - Support of relations of all types - Filtering - Support of inequalities ## Plans - Mutaions - [Aliases](https://graphql.github.io/learn/queries/#aliases) - [Directives](https://graphql.github.io/learn/queries/#directives) - [Fragments ](https://graphql.github.io/learn/queries/#fragments) → [Link ](https://github.com/intersystems-community/GraphQL) to the project repository → [Link ](http://37.139.6.217:57773/graphiql/index.html) to the demo server Issues Pull Requests are very welcome. Keep an eye on our project updates! Just for the record, GraphQL access to Cache has been supported in EWD.js and EWD 3 (the fore-runners to QEWD.js) since 2015:https://groups.google.com/forum/#!searchin/enterprise-web-developer-community/graphql%7Csort:date/enterprise-web-developer-community/okC6T2W-Sfk/UomKqSrwBQAJhttps://groups.google.com/forum/#!searchin/enterprise-web-developer-community/graphql%7Csort:date/enterprise-web-developer-community/aO1-9mE1FOE/ebdUS8ZbBAAJ And, any stats, Rob? Is it the most popular API in EWD and EWD3? And why it is not supported in QEWD? As I release my code as Open Source, I've no idea of usage stats. There's growing interest in the use of GraphQL in the community I work within these days, for all sorts of reasons.QEWD is a re-badging of EWD 3, so yes, it is supported in QEWD - which of course is straightforward since QEWD is JavaScript/Node.js and can just use the Facebook JavaScript GraphQL module.Just making the point that GraphQL and support for it in Cache isn't something new, as those who follow our work at M/Gateway will know :-)Rob Actually I'm impressed how quickly you guys introduce in QEWD new trendy approaches for development. Though there is a difference here: what @Arutunyan.Gevorg published supports Caché Objects and I doubt if QEWD does. Is there any Caché Objects support/mapping in QEWD? QEWD supports Cache Objects for those who wish to use them via the cache.node APIs for Cache Objects.:https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=BXJS_objectsYour handler logic (which runs in a QEWD Worker process) has access to these APIs via the "this.db" object, eg this.db.invoke_classmethod()The following post is a bit old and refers to how you used these APIs in EWD.js. However, change the invocation to this.db.xxx() and the examples should work in QEWD:https://groups.google.com/forum/#!searchin/enterprise-web-developer-community/invoke_classmethod%7Csort:date/enterprise-web-developer-community/iVR-z_Lxs9I/PPS26Lp5RskJPersonally I prefer to use the Cache database as a persistent JSON database / Document Database, via the ewd-document-store abstraction that is used by QEWD, and integrate that storage into JavaScript Objects. But Cache Objects will work with QEWD and GraphQL just fineRob Hi Rob,I have searched and read through all training materials only for this answer. Probably, it is a good idea to have a separate slide which shows how QEWD can make use of cache object scripts from the handler methods with examples.This may be useful for:1. Applications written in Cache object scripts over the years with business logic that is not easy to change in few years of development.2. Applications using a middle layer and they want to replace it with QEWD to make use of micro-service architecture.3. New development mainly focusing on back-end logic in Cache database with object oriented programming.4. And of course, to make use of QEWD while enjoying the Cache object oriented programming style rather than using it as a document store (I think Cache is not mainly used for document store, but because of its value added features and strengths). Also, to adopt QEWD in Cache based ERP applications, integration of QEWD with Cahce object script is vital.Any training materials, slides or GitHub source with working examples to use QEWD and cache object scripts is highly recommended.Thanks,Jose After import an error occurred as below.---------------------------Studio---------------------------ERROR #6301: SAX XML Parser Error: invalid XML encoding declaration '' while processing Anonymous Stream at line 1 offset 32 > ERROR #5490: Error running generator for method 'DispatchMap:GraphQL.REST.AbstractREST'ERROR: %CSP.REST.cls(DispatchMap) of generated code compiling subclass 'GraphQL.REST.AbstractREST' > ERROR #5030: An error occurred while compiling class 'GraphQL.REST.AbstractREST'---------------------------OK ---------------------------I am using Cache 2017.2.2.Any pointers will be a great help.Thanks, Strange, as that should not be hit for AbstractREST at all.Does you class looks the same as in repo?Just compiled successfully on: Cache for Windows (x86-64) 2017.2 (Build 744U) Fri Sep 29 2017 10:58:27 EDT Yes, same version. Just downloaded from repo and imported GraphQL.xml.Version: Cache for Windows (x86-64) 2017.2.2 (Build 865) Mon Jun 25 2018 10:45:31 EDTAlso in Cache for Windows (x86-64) 2017.2 (Build 744) Fri Sep 29 2017 11:10:05 EDTand getting the same error when import and compile GraphQL.xml. You use 8bit Caché. Maybe you need to convert GraphQL.xml into your local encoding.Or use Unicode Caché. Thanks Eduard, now it is working in two different versions.1. Cache for Windows (x86-64) 2017.2.2 (Build 865U_SU) Mon Jul 9 2018 14:31:14 EDT USER>Write $system.Version.IsUnicode() 12. Cache for Windows (x86-64) 2018.1 (Build 179) Tue Sep 4 2018 00:04:51 EDT USER>Write $system.Version.IsUnicode() 0 Really interesting stuff that I would like to introduce but there seem to be no new developments for a long time now. Has this development fizzled out? What features are you most interested in? Unless its not documented, I think he meant features like mutations and subscriptions.Specially mutations, anything else have a workaround. Mutations, mainly. Is GraphQL support, or will it become, an official part of the product? I have not heard about such plans, maybe when this technology becomes more stable and widely adopted. Extremely uneducated question: If we have a global with its nice tree structure (like for example that Persons tree built in Globals Introduction by InterSystems Learning Services) without specific schema (e.g. class definition), would graphql be able to be used to make specific condition/wild-card based queries? Current implementation resolves GraphQL query into SQL, so classes/tables are required. This is our advantage as we don't need to write resolvers manually. That said, if you want to provide custom schema - you can, as our GraphQL implementation includes GraphQL parser so you can use parsed AST to write your own resolver. This looks very promising. I've got the testing shell up and running returning data from the Sample namespace but how do i "generate a schema for all classes in the namespace" as mentioned in your instructions? My Documentation Explorer just reports "NO SCHEMA AVAILABLE" What does running this code write ##class(GraphQL.Utils.Schema).GetSchema().%ToJSON() return for you? It generates tons of JSON looking data. Is there a trick to getting the GraphiQL to get to the schema? Any settings or such? This is the schema. Do you uses GraphiQL packaged with the repo? yes, I did No worries, I got it working after a fresh reinstall. Probably did something wrong the first time around
Announcement
Anastasia Dyubaylo · Oct 9, 2018

InterSystems Global Summit 2018 Highlights

Hi Community!I want to share with you my impressions of Global Summit 2018.It was my first time in the USA and my first Global Summit. And I can say that it was really amazing trip and a great experience!And here are some key highlights of my choice:1. This year we had a special Global Masters Meeting Point under my direction. There, all the summit participants could learn more about our gamification platform, ask questions, share ideas, check out some samples of rewards and, of course, register and get their first points. What is more, many of our Advocates were awarded with special Badges for their active participation in Global Masters and Developer Community life. It was great to meet Developer Community Heroes in person, congratulations again! 2. Developer Community Flash Talks and the launch of InterSystems Open Exchange — a marketplace of Solutions, Interfaces, Plugins and Tools build with and build for InterSystems Data Platforms. Big applause goes to all the speakers, @Eduard.Lebedyuk, @Dmitry.Maslennikov, @John.Murray, @Daniel.Tamajon, @Evgeny.Shvarov and @Peter.Steiwer, thank you guys! 3. Global Summit Partner Pavilion was really full of people involved in interesting conversations. Please find more photos on DC Twitter. 4. A great activity on social networks, especially on Twitter. Here are some facts:More than 1,000 Tweets were posted with our special #GlobalSummit18 hashtag and garnered about 2 million impressions. The Hashtag #GlobalSummit18 was in top 20 Twitter Hashtags worldwide (11th place).We had 9 Broadcasts in DC Twitter and 2 Broadcasts in YouTube and in total more than 3,000 views. And...What about you? We're waiting of your stories from InterSystems Global Summit 2018!Please share your impressions with our special #GlobalSummit18 hashtag!
Announcement
Uwe Hering · May 19, 2018

Dive into InterSystems IRIS @ CEBIT 2018

Hi all, Anyone who is planning to attend CEBIT 2018 in Hannover should make sure to join us on June 13 from 2pm to 5pm for a technical deep-dive into our new InterSystems IRIS Data Platform™ technology. We will explore how InterSystems IRIS paves the way for innovation by supporting open analytics, sharding, docker and cloud deployments, to name but a few. Expect an interactive learning experience with insightful presentations and live demos. To register, simply send an email to marie-laure.kinkel@intersystems.com. The event is free of charge, but has only a limited number of seats available, so make sure to sign up NOW! We will also provide you with a FREE CEBIT ticket upon registration. What’s more, you will also get the chance to delve into InterSystems IRIS yourself: Sit down in our Sandbox area at stand C49 in hall 13 and take InterSystems IRIS to the test. See and discuss the agenda below. Session I – Data-driven Business Models Session II – InterSystems IRIS Technical Deep-dive ​View the original agenda of the event in German here. We look forward to welcoming you in Hannover! Sincerely,InterSystems CEBIT Team
Article
Niyaz Khafizov · Jul 27, 2018

Load a ML model into InterSystems IRIS

Hi all. Today we are going to upload a ML model into IRIS Manager and test it. Note: I have done the following on Ubuntu 18.04, Apache Zeppelin 0.8.0, Python 3.6.5. Introduction These days many available different tools for Data Mining enable you to develop predictive models and analyze the data you have with unprecedented ease. InterSystems IRIS Data Platform provide a stable foundation for your big data and fast data applications, providing interoperability with modern DataMining tools. In this series of articles we explore Data mining capabilities available with InterSystems IRIS. In the first article we configured our infrastructure and got ready to start. In the second article we built our first predictive model that predicts species of flowers using instruments from Apache Spark and Apache Zeppelin. In this article we will build a KMeans PMML model and test it in InterSystems IRIS. Intersystems IRIS provides PMML execution capabilities. So, you can upload your model and test it against any data using SQL queries. It will show accuracy, precision, F-score and more. Check requirements First, download jpmml (look at the table and select suitable version) and move it to any directory. If you use Scala, it will be enough. If you use Python, run the following in the terminal pip3 install --user --upgrade git+https://github.com/jpmml/pyspark2pmml.git After success message go to Spark Dependencies and add dependence to downloaded jpmml: Create KMeans model PMML builder uses pipelines, so I changed the code written in the previous article a bit. Run the following code in Zeppelin: %pysparkfrom pyspark.ml.linalg import Vectorsfrom pyspark.ml.feature import VectorAssemblerfrom pyspark.ml.clustering import KMeansfrom pyspark.ml import Pipelinefrom pyspark.ml.feature import RFormulafrom pyspark2pmml import PMMLBuilder dataFrame=spark.read.format("com.intersystems.spark").\option("url", "IRIS://localhost:51773/NEWSAMPLE").option("user", "dev").\option("password", "123").\option("dbtable", "DataMining.IrisDataset").load() # load iris dataset (trainingData, testData) = dataFrame.randomSplit([0.7, 0.3]) # split the data into two setsassembler = VectorAssembler(inputCols = ["PetalLength", "PetalWidth", "SepalLength", "SepalWidth"], outputCol="features") # add a new column with features kmeans = KMeans().setK(3).setSeed(2000) # clustering algorithm that we use pipeline = Pipeline(stages=[assembler, kmeans]) # First, passed data will run against assembler and after will run against kmeans.modelKMeans = pipeline.fit(trainingData) # pass training data pmmlBuilder = PMMLBuilder(sc, dataFrame, modelKMeans)pmmlBuilder.buildFile("KMeans.pmml") # create pmml model It will create a model, that predicts Species using PetalLength, PetalWidth, SepalLength, SepalWidth as features. It uses PMML format. PMML is an XML-based predictive model interchange format that provides a way for analytic applications to describe and exchange predictive models produced by data mining and machine learning algorithms. It allows us to separate model building from model execution. In the output, you will see a path to the PMML model. Upload and test the PMML model Open IRIS manager -> Menu -> Manage Web Applications -`> click on your namespace -> enable Analytics -> Save. Now, go to Analytics -> Tools -> PMML Model Tester You should see something like the image below: Click on New -> write a class name, upload PMML file (the path was in the output), and click on Import . Paste the following SQL querie in Custom data source : SELECT PetalLength, PetalWidth, SepalLength, SepalWidth, Species, CASE Species WHEN 'Iris-setosa' THEN 0 WHEN 'Iris-versicolor' THEN 2 ELSE 1 ENDAs predictionFROM DataMining.IrisDataset We use CASE here because KMeans clustering returns clusters as numbers (0, 1, 2) and if we do not replace species to numbers it will count it incorrectly. Please comment if you know how can I replace сluster number with a species name. My result is below: There you can look at detailed analytics: If you want to know better what is true positive, false negative, etc, read Precision and recall. Conclusion We have found out that PMML Model Tester is very useful tool to test your model against data. It provides detailed analytics, graphs, and SQL executor. So, you can test your model without any extended tool. Links Previous article PySpark2PMML JPMML ML Pipelines Apache Spark documentation Note that in InterSystems IRIS 2018.2, you'll be able to save a PMML model straight into InterSystems IRIS from SparkML, through a simple iscSave() method we added to the PipelineModel interface. You can already try it for yourself in the InterSystems IRIS Experience using Spark.Also, besides this point-and-click batch test page, you can invoke PMML models stored in IRIS programmatically from your applications and workflows as explained in the documentation. We have a number of customers using it in production, for example to score patient risk models for current inpatient lists at HBI Solutions. The article is considered as InterSystems Data Platform Best Practice.
Announcement
Jeff Fried · Nov 12, 2018

New release cadence for InterSystems IRIS

Note: there are more recent updates, see https://community.intersystems.com/post/updates-our-release-cadence InterSystems is adopting a new approach to releasing InterSystems IRIS. This blog explains the new release model and what customers should expect to see. We laid this out at Global Summit at the end of the InterSystems IRIS roadmap session and have received a lot of positive feedback from customers. With this new model we offer two release streams: 1) An annual traditional release that we call EM (for Extended Maintenance) 2) A quarterly release that is tagged CD (for Continuous Delivery) and will be available only in a container format. Why change? Speed and predictability The pace of change in our industry is increasing, and this model ensures that we can publish the latest features very quickly to be responsive and competitive in the market. Many customers have told us they want two things: Quicker time from requesting a new feature to having it available A predictable schedule that allows them to plan for updates Our new release cadence, inspired by continuous delivery principles, is similar to two-stream models used at many major software companies and a substantial fraction of enterprise-ready open source projects. Those that have successfully adopted this approach resoundingly report that they have higher quality and lower-risk releases, as well as a faster response time. What’s not changing? Traditional releases are the same Traditional releases (the "EM" releases) work the same way that our customers are used to. They receive ongoing maintenance releases, are the basis for adhocs as needed, and are supported on all platforms. Full product installation kits are available through the WRC Software Distribution portal as usual. Field tests will be available for major releases as we have done in the past. Maintenance releases will be made available for EM releases using the same ground rules we’ve used traditionally. The difference is that these are released once per year, at a predictable time. Version 2019.1 of InterSystems IRIS is scheduled for March of 2019, version 2020.1 is scheduled for March 2020, and so on, as shown in the diagram below. New quarterly releases are container-only Every three months, new features and functionality will be available via a new quarterly release stream, denoted with a “CD”. For example, InterSystems IRIS version 2018.2 CD is scheduled for November 2018, version 2019.1 CD is scheduled for February 2019, version 2019.2 CD is scheduled for May 2019 and so on, as shown in the following diagram. There are restrictions on these CD releases: They are only available as container images, using the Open Container Initiative (OCI) format. This is widely used and supported by many companies including Docker, Amazon, Microsoft, Google, and IBM. They only run on OCI compatible infrastructure. Docker is the most common OCI runtime, so InterSystems provides and supports docker containers built with the Ubuntu Linux kernel. This runs on a wide variety of platforms: all major cloud platforms (Amazon AWS, Microsoft Azure, Google GCP, IBM cloud), essentially all flavors of Linux, Windows Server 2016 and 2019, and more. InterSystems supports deploying containers on Windows 10 and Mac OS for development only, using Docker-for-windows and Docker-for-mac respectively. (The most notable platform that does not currently support OCI containers is AIX.) Because these are containers, there is no install and no image upgrade. You can use the container images provided by InterSystems and compose your own images using them. To deploy, you simple replace containers. If there are any data upgrades needed, InterSystems will supply these along with the release. With CD releases, InterSystems will not provide maintenance releases, security fixes, or adhocs. If you want to get a change, you can simply take the next release. There is a new release with the latest changes every three months, so you don’t need to wait longer than that for important fixes. CD releases are fully supported by InterSystems for development, test, and production. Along with each CD release, InterSystems will have a preview program and provide preview images ahead of the final release. Preview images are supported for development and test purposes, but not for production. Although Containers are relatively new, they are now widely used and provide many benefits. Customers do not need to use the CD releases or adopt containers, but there are many resources available from InterSystems to help with using InterSystems IRIS in containers (including multiple online videos) as well as a large ecosystem around containers in the industry at large. In addition to providing rapid delivery of new features, CD releases will help with the predictability and stability of the traditional (EM) releases. The first CD release of the year has a corresponding EM release (which is the same except for any platform-specific capabilities), and these include all the functionality of the previous CD release plus more. Developers can work with CD releases and be confident that their code will work with traditional releases as well. Even if you never touch a CD release, you can track what features are released with InterSystems IRIS each quarter and plan confidently. Hello @Jeffrey.Fried, quick question.The CD releases will be Docker Images downloadables only from WRC or will be another public site? (Docker marketplace)Thanks Hello @David.Reche - we will have these available in public marketplaces, for sure. We will announce here when that is available, currently the image is available through WRC. Hi @David Reche - We do plan to make these available from the docker store as well as the major cloud marketplaces and of course via the WRC. The idea is to fit smoothly into the DevOps pipelines and toolsets our customers use.We're working on this, so stay tuned, and in the meantime please use the WRC.Any and all feedback welcome.