quarta-feira, 14 de novembro de 2007

The End

Since no one really reads, this blog is over.

The house fell for once.

segunda-feira, 5 de novembro de 2007

Lists with Caché

Caché Lists - an object oriented approach

Unlike Java, the Intersystems Caché doesn't have large API to manipulate data structures of collections (lists, queues, sets, etc). In fact, when we work with objects in Caché, we can rely only on two pre-defined structures: lists and arrays.

For Caché, lists are, as the name implies, lists of objects (instances of classes you write) r lists of basic data types (as %Integer or %Float). Each element of a list is associated with a position in it. Caché lists are very similar to Java lists (comparing with List interface, about implementation, we can not make good comparison, since Java can have lots of implementations, as ArrayList orLinkedList).

With Caché, the abstract class %Collection.AbstractList gives us the basic interface to work with lists. Adapted from docs, the code snippet below shows how to create a list, 4 elements insertions, and how to iterate the list:


; creates new list
Set list=##class(%ListOfDataTypes).%New()
; here we add four elements in the list, four strings
Do list.Insert("DArtagnan")
Do list.Insert("Athos")
Do list.Insert("Porthos")
Do list.Insert("Aramis")
; how to iterate the list
For i=1:1:list.Count() Write list.GetAt(i),!



Caché arrays are a little bit different. They seem a lot like Java Map's interface. A Caché array needs, besides an element, a key to associate with it. Through this key, you can access the element in the structure. This kind of structure are useful to implement hash tables, as Java's Hashtable class.

As examples are the best way we can learn, below there is one, adapted from the documentation on class %Collection.AbstractArray, the 'interface' of Caché arrays:

; creates new array 
Set arr=##class(%ArrayOfDataTypes).%New()
; put itens in array - the second parameter is the 'key'
Do arr.SetAt("red","color")
Do arr.SetAt("large","size")
Do arr.SetAt("expensive","price")
; how to iterate arrays
Set key=""
For Set value=arr.GetNext(.key) Quit:key="" Write key,":",value,!



(Just pay attention to SetAt method, similar to java.util.Map's put. The order of parameters is inverted in those two. As put has the key as first parameter, SetAt the key is the second.)

With both examples above, we use basic data types (%String, in both cases) as elements in lists and arrays, but we could have used objects. To use objects, we can just swap %ListOfDataTypes and %ArrayOfDataTypes for %ListOfObjects and %ArrayOfObjects.

When we use collections as classes properties, we can explicitly declare the property as an object of Array/List classes or we can declare them as below:


; a %String list
; instead: Property Colors As %ListOfDataTypes
Property Colors As List Of %String


Just keep in mind that arrays and lists classes are classes of Serial type, i.e., they inherit from %SerialObject, therefore, they are 'serialized' inside other classes when they are persisted. If a list is too big, or even if the list has not that much elements, but the elements are %SerialObject too, there is a chance to occur a error.

Consider the class below:


Class Test.Bang Extends %Persistent [ ClassType = persistent ] {

Property alist As list Of %String;

ClassMethod test(amount as %Integer)
{
set a = ##class(Test.Bang).%New()
w a.%Save()
For i=1:1:amount {
do a.alist.Insert(i)
}
w a.%Save()
}
}


You can test calling "test method" with different values. With Caché 5.2, if you call "do ##class(Test.Bang).test(10000)", you can check the error. I just heard that newer versions, as 2007, the maximum string size was increased, so this kind of error will occur less frequently (although I didn't check this info myself).

terça-feira, 30 de outubro de 2007

How I miss ROWNUM sometimes...

Today I needed to calculate the average from a field from a table in Intersystem's Caché. The logic was very simple: I just neede the average of prices of last N purchases, after some date.

Oracle has the pseudo-column ROWNUM, which makes my problem very easy to be solved. I would have something like:

SELECT AVG(PRICE)
FROM PURCHASE
WHERE DATE > '01/01/01' AND ROWNUM <= 20
ORDER BY DATE DESC


In this simple example, we calculate the average of price of last 20 purchases made after day 01/01/01. Quite simple, no?

I searched for something similar in Caché, but I couldn't find it. I found some related stuff, like a variable for the number of lines a query resulted, or the TOP statement, used within SELECT. Unfortunatelly, nothing helped me much, so I had to open a cursor (or I could have used a ResultSet, if I wanted to), and iterate through the results, and calculate the average myself.

So, the query took this form:

SELECT TOP 20 PRICE
FROM PURCHASE
WHERE DATE > '01/01/01'
ORDER BY DATE DESC


See the 'TOP 20'. It that for the query I just want the first 20 records. Unfortunatelly, we can not use it on WHERE statement, so we can not use it for the AVG function.

I will not describe how I iterate through a cursor and calculate the average, since it's quite trivial, and I'm tired now.

Today, I'm done.

segunda-feira, 29 de outubro de 2007

Execute code dinamically with Caché

...Or How to evaluate and execute an user input's expression

The scenario: with the application, an user can input some math expressions, using variables previously defined (by himself or another users). These variables might be simple or they might depend on some parameter. An example of expression is below:

(0.50 * ([VAR1(I)] / [VAR1(0)])) + (0.50 * [VAR2])

where:

  • VAR1(I) is a variable, which depends on parameter "I".

  • VAR1(0) is a variable (in fact, the same variable above), but depends on another parameter, "0".

  • VAR2 is a simple variable.


By convention, variables are written between "[]". Most variables are in fact, historical series of values, and those parameters indicate the specific time/period to be considered.

The problem: given a time interval, I need the value the expression evaluates to. Or, with another terms, I need to execute the expression, as if it was part of the program. Some other script languages have the eval function, which takes a text (i.e., an String) and executes it as if it were program code. In this link, an example of JavaScript's eval(). With Caché Object Script, we have the command XECUTE, which does the same thing, it gets the content of an string and executes it, i.e., it does macro substitution.

Well, even with this feature, I still need to identify within the %String, each variable. To make this, I wrote a little function, which I put in a .MAC file:

(A snapshot of the function, the text source code is here.)

For this function, we pass as parameters: the expression, two characters used to delimit what is considered a variable, (in our case, the characters "[" and "]"), a %String to be place where each variable was, and another character to delimit parts of %String (something used a lot with MUMPS-like systems, to create lists - without list functions - see here some discussion.)

Using the expression above in this function, with "?" as the %String to replace the variables, and "~" as delimiting character, we get:

(0.50 * (? / ?)) + (0.50 * ?)~VAR1(I)~VAR1(0)~VAR2

The next step, we get from the system the value of each variable, and replace it within the expression. I will not show these steps, since they are very specific to our problem. After that, our expression String is something like:

(0.50 * (12.3 / 10.88)) + (0.50 * 10)

Note that the expression is mathematically correct, and the precedence of operator is explicit with the use of parenthesis. This REALLY is something to be done, since Caché DOES NOT follow math conventions of operators precedence, as I already posted.

Now, we can finally execute the expression. First, we set the command SET inside a %String, as follows:

SET someVariable = "SET result = " _ stringWithExpression

And then we call XECUTE:

XECUTE someVariable

That's it! The value of the evaluated expression is in variable "result"! Using XECUTE is very handy, but you might pay attention to not abuse it's use, since it can turn your code into a really great mess!

segunda-feira, 15 de outubro de 2007

Uploading files with Caché and CSP

Uploading files with Caché and CSP - or... My first CSP

As I already said here (or not), my work is almost all done with Intersystems' Ensemble (and consequently with Caché, since Ensemble is built upon it). Therefore, I am not generally involved with user interfaces and these kind of stuff, but this week I needed to do a web page to upload files to my server. So, lets learn some CSP - or Caché Server Pages. In fact, I didnt reaaaally learned them, I just got the stuff to do what I needed. And I thought it'd be nice to share the experience with you ;)

As CSP Introduction page says, you have two paths if you´re willing to work with CSPs: you can write Html pages and embed them with csp tags, much like JSP (Java Server Pages) or PHP, saving them with .csp extension; or you can write a Caché's class, where you can output the html, the same approach of Java Servlets.

Within examples (in namespace SAMPLES of Caché) there is a .csp file with an example of upload (as you can guess, it is csp/samples/upload.csp). Taking this as base, I adapted the necessary to my needs.



(Upload example page, and the resulting page after an upload)

Well, as I couldn't place the source code here at blogger, in a good way, I will put an image, and let a link to source code text.

(Image with source code)

The logic behind this csp page is very simple: when you open this page, calling from a parent window, you pass the request parameter 'campo', if not, it will show you nothing. Then, the csp code checks if a file was uploaded, if not, it renders an html form with fields for upload (the form's submit points to the same csp); if the file was already uploaded, it sets the file name to an html field from the parent window who called this csp, and shows a link to close this current window.

Lets view with more details some of this.

<CSP:CLASS INCLUDES="SOMEINCFILETOINCLUDE">

Here we point to an .INC file (without the inc extension) that we will use in the page, i.e., we are including this INC file. In Caché, INC files are used to define macros (like C/C++ macros), and are very used to both implement routines and define constants.

/***********************************

<csp:if condition='($data(%request.Data("campo",1)))'>

Here we show an specific tag from csp, which means a condition, the famous 'if'. The condition is the value of attribute 'condition', of 'csp:if' tag, and it may contain COS (Caché Object Script) code, returning true or false. In our code, we are just checking if the http request (%request object) has as one its parameters a field named 'campo'. More info about %request object can be found at %CSP.Request class doc page.

/***********************************

<csp:if condition='($data(%request.MimeData("FileStream",1)))'>

Other if, but a little different. This time we do not check the http request parameter with %request.Data, but with %request.MimeData. The reason is because this time we are checking if a file was sent, using MIME.

/***********************************


File uploaded: <b>#(%request.MimeData("FileStream",1).FileName)#</b><br>
Size: <b>#(%request.MimeData("FileStream",1).Size)#</b><br>
Type: <b>#(%request.MimeData("FileStream",1).ContentType)#</b><br>


When we sent a file, %request.MimeData("PARAMETERNAME",1) return an object %CSP.BinaryStream. The above code shows on screen some info that we can get from this stream object. Note that the values to be rendered on the screen are between '#(' and ')#'.

/***********************************

<script language="Cache" runat="server">

Here we declare a code block of COS to be executed at the server. In this block, if we want something to be shown in the resulting html, we must write at the standard output, using Write command.

/***********************************

set dname = $$$SomeDirectory

Still within the block we opened above, here we create and set a value to the variable 'dname'. This value is a macro, which is indicated by the three $ ($$$SomeDirectory). This macro should be defined in some of .INC files we imported, like commented above.

/***********************************


Set stream=##class(%FileBinaryStream).%New()
Set stream.Filename= dname _ fname

do stream.CopyFrom(%request.MimeData("FileStream",1))
do stream.Flush()
do stream.SaveStream()


Here we create an stream to a file (class %FileBinaryStream), and we set it to variable 'stream'. Then we set the file's name, and use the method CopyFrom, to copy the content of the stream sent through http, to the file.

/***********************************

<csp:else>

Here we finish the 'if' block, and start the 'else' block.

/***********************************

<form enctype="multipart/form-data" method="post" action="este_arquivo_csp.csp">
Arquivo: <input type=file size=30 name=FileStream>
<p>

<script language="Cache" runat="server">
New bytes
Set bytes="<input type=""hidden"" name=""campo"" value=""" _ %request.Data("campo",1) _ """ > "
Write bytes,!
</script>

<p> <ul><input type="submit" value="Upload file"></ul> <p>
</form>


The 'else' block has the definition of an html form, to upload the file to the server. Note that inside de html form definition, we mix some Caché code, to place a hidden field within the form, to keep the value of parameter 'campo', which was passed. This is one solution, another one (maybe more 'elegant') would be use sessions, but as this is very simple case, I didn't check how to manage sessions with CSPs (but of course, you can explore it yourself).

/***********************************

As this post got bigger than I expected, I will finish it here. Within CSP file there are other features (as some javascript, and COS directory apis), but I will not comment them now. Besides, as I simplified the csp a lot, and I removed all stylish stuff. For you to use it, you will have to change some things, like the import of .inc file.

That's all folks.

sexta-feira, 5 de outubro de 2007

Some curious stuff from Caché

As Intersystem's Caché Object Script is a "typeless language", sometimes it produces curious results, especially if you, reader, is more used to math notations than "stuff" of IT.

As an example, lets open a Caché's terminal window, and declare two variables*:

USER>set x = "5.10"

USER>set y = "5.1"

USER>write x
5.10
USER>write y
5.1

Remembering that Caché, (as good and old C) treats 1 (one) as TRUE, and 0 (zero) as FALSE*:

USER>write x = y
0
USER>write x > y
0
USER>write x >= y
1




It all means that (x = y) is false, (x > y) is also false, but (x >= y) is true! Or, "x" is not equals to "y", and "x" is not greater than "y", but "x" is greater than or equal to "y", hohohoho 8-)

Lets give some thoughts about it:

  • The first statement (x = y) is false because in this case, Caché assumes an iguality test between two Strings, and String "5.1" is different from String "5.10"

  • The second statement (x > y) is false, not because Caché compares String's lengths (x < y also returns false), but because when the operator ">" is used, Caché dynamically casts the variables' values to numeric values, and because "x" IS NOT GREATER THAN "y" ("x"'s value is numerically EQUAL TO "y"'s value, since 5.1 = 5.10), the overall result is false.

  • Well, by now you might already have figured out why (x >= y) returns true, right? When using "<", "<=", ">", ">=" operators, Caché tries to cast the variables to numeric values, and because numerically "5.1" IS EQUALS to "5.10", the expression (x >= y) is true.

All this might confuse some people not used to programming, just as Caché's operators precedence, which I already talked about.

* Some tips: the "set" command is used to initialize/assign a value to a variable, and the "write" command (just as it says) writes to the current output device, in our case, the terminal window.

sexta-feira, 28 de setembro de 2007

Ensemble Best Integration Software in the market? Not really...

Some weeks ago I was searching something with Big G (I don't remember exactly what), and I found this news: Report recognizes Ensemble as best Integration Software of the market (the news is in portuguese, and I just adapted the title). This really surprised me! I'm not saying Ensemble is a bad software, but the fact is, that it is faaar from being what I would like to work with (OK, there are parts of it that are reaaaally terrible).

I followed the link, and after I have read the page, I just realized that this, as many news calls, is (at least) incomplete. Lets to the facts: A report really pointed this 'leadership' of Ensemble. However, the report aims only for american Healthcare IT business, which is much more restrict than the term 'of the market' means.

The company which published this report is KLAS, which in their words "is the only research and consulting firm specializing in monitoring and reporting the performance of healthcare’s information technology (HIT) vendors". The report is not completely available (at least, not free at the site), but you can get a sample of it here.

As it was done, the result of this report didn't surprised me that much. Caché (and therefore, Ensemble, which is built upon it) is a very strong player in the market of Healthcare IT, inherited from MUMPS. Although Intersystems doesn't like to mention it. Caché is an implementation of MUMPS (with some new features, but the 'spirit' is the same).

IMHO, if this report was made not focusing this specific market, Ensemble would not be that well. I don't have data to confirm this, so if someone knows some reliable data, tell me! (but please, I don't want those 'white papers' from their own company, which is probably written by marketing division)

segunda-feira, 10 de setembro de 2007

Web Services with Caché/Ensemble - The Quest - chpt 3

And this is the end of trilogy...

As I said at the end of last post, the chapter 2, when I was writing the SOAP-Body manually, the requisition message was correctly understood by the Web Service. However, for some unknown reasons, the response message was not returning correctly to my code in Caché Object Script. At first, I thought it wasn't working at all, but after I analyzed the TCP stream with a sniffer, I realized that the Web Service was returning correclty. The problem was with Caché/Ensemble, who didn't understand the answer message.

Wondering if the problem wasn't with my modifications, I tested all the WS Client classes without any modifications, but the error was the same. Conclusion: all work so far was for nothing. So, after analyzing Caché's source, and through some little clues the error on screen, I realized that Caché was giving error because it tried to instantiate a class based in WS answer message, but it failed:


(an example of a response message, informing the request was correctly processed)

By then, tired of navigating through source code and bad written documentation, I decided to went down: since I the Caché's %SOAP package could not work fine with the given Web Service, I was going down in the protocol stack, to make it work. Caché doesn't have 'native' classes to deal straight with HTTP (at least, I didn't find any), but Ensemble has an HTTP "Adapter".

(One parenthesis: Adapters in Ensemble are pieces of software which have the function of connecting Ensemble with other systems/technologies. The documentation on this Adapters is really bad, as an example, in this page listing the Adapters, the HTTP is not even listed, and a lot of Adapters there, simply doesn't exist here, in my installation. Freaking...)

After a little testing, I discovered that I was not able to use HTTP Adapter with the Web Service, because this Adapter doesn't let me set a header SOAPAction, in HTTP headers, which is mandatory for this Web Service to work.

As I couldn't use HTTP classes, I went one step below in protocol stack, and use TCP. Ensemble's TCP Adapter is nothing more than a wrapper for Caché's Socket classes, something I already talked about in two previous posts, here (about client sockets) and here (about server sockets).


(protocol stack - almost at IP level =P )

The good thing in using TCP is that you have control over everything, of the HTTP headers, the SOAP XML message, etc... The bad thing is that in order to use it, besides you must understand a great part of all this stuff, you have to write all manually. But as the Caché/Ensemble tools availables were just giving me headache (and errors), I had not much choice. XD

Take a look how it ended (more or less, here I simplified some stuff):



Finally, with a horrible code and using some anti-patterns, all worked. And all that with Ensemble, which, here, some call "Integrator"... I wonder if it wasn't...

quinta-feira, 6 de setembro de 2007

Web Services with Caché/Ensemble - The Quest - chpt 2

And the quest continues...

I had based my implementation of all internal stuff of Ensemble
at previous Web Service/WSDL the third-party company gave me. (If you wanna know what stuff I mean, it's BPEL e Ensemble's "Business Operations".)

With the previous WSDL, the Caché's WS Client wizard generated all the structures as classes, i.e., if I had to send an structure "Person", I would have a generated class "Person", with its properties "Name", "Age", etc..., and the method's parameter would be an instance of this class. However, with the new WSDL, the wizard (I dont know why) generated the methods with each property of the structure as a separated parameter. If before it was:

updatePerson(p as Person)

Now it generated:

updatePerson(name as %String, age as %Integer, ...)

And OF COURSE it broke down all my previous work. For me not to have to refactor all other classes, I thought it would be better to change manually the classes generated by the wizard, adapting them for the classes I had.

So, I continued my quest, changing the Web Client class generated:

(before)

(after)

Well, as naive as someone can be, I thought just this would work. I compiled and tested... and didnt work. To not extend the description of my struggle, I found out that with my changes, it was not writing correctly the Body of the SOAP Envelope, as the Web Service expected, with the namespaces correctly declared.

Looking for some solution, I saw the property "WriteSOAPBodyMethod" from the class %SOAP.WebBase, which is superclass of %SOAP.WebClient. This property allows us to define a method to write a custom Body for a SOAP message.

Using it, we have:

(writing custom SOAP-Body - advice: do it as last solution)

With this little "trick", I finally got all my other code intact and working, and it really called the Web Service correctly. But... there was a catch, always a catch...

The quest was not over yet...

To be continued.

quarta-feira, 5 de setembro de 2007

Web Services with Caché/Ensemble - The Quest - chpt 1

I have been creating SOAP Web Services with Caché/Ensemble for some time now, and it is very easy, it is as easy as use an annotation with Java, the difference is that you have to extend a class (not a big problem, since Caché supports multiple inheritance) besides annotating the method you want to expose as a service. So, I thought that it would be nice and easy to use Caché/Ensemble as a Web Service client. I was wrong.

It all started when I finally got in hands the WSDL describing some services from a software the company had purchased. These services were needed to allow ours systems to integrate with the new software (which is almost a black box to us).

Since I had made some tests creating Web Service clients for another services and WSDLs, I was pretty confident it would be easy. Too wrong, I was, as Master Yoda would say. Using the WS Client generator Wizard, I just got an error screen. Unfortunately, as you can see below, Caché/Ensemble didn't give me quite nice and enough information, so I got some doubts: the WSDL I got from the third-party software-house was broken/wrong/f***ed up, or Caché just doesn't understand some stuff? (At this point, both hypothesis had the same probabilites to me.)


(Screen with WS client generator wizard error)

So, I thought I'd test the Web Service using soapUI, a program made with Java, that allows you to test SOAP Web Services. I talked about it in last post.

Using soapUI, I could not only create the correct structure of SOAP messages, but also execute the service. This led me the conclusion that there were something wrong with Caché's wizard. As I am a curious person, I tried to figure out what was causing trouble in that WSDL. After half day lost, comparing it with other WSDLs and navigating into specs, the answer finally arised: the request message had two elements, when it usually have just one (usually the message object itself):


(the piece of WSDL which was causing me trouble)

I just erased the second element tag from the message, and the wizard worked! I felt relief, since in this WSDL there were other methods (which didn't cause me problems), and the wizard just stopped when it encountered an error. Unfortunately, my relief didn't last long...

With an older version of this Web Service (one that was given to me during the third-party company development), Caché's WS Client wizard worked well, so I had all my implementation based in it. Of course I was expecting some changes, but I didn't thought there were so many (and so big)... I was wrong, very wrong...

To be continued...

terça-feira, 4 de setembro de 2007

soapUI

In this post I'll talk about a very usefull software I usually use, specially when I have to test SOAP Web Services. The software is called soapUI, and is open-source, with an option of paying for support. Info about installation and first steps is here.

So, what can you do with this program?

Given a WSDL, it will create for you XML request messages, that you can fill up with some data, and submit it to the Web Service, and see the resulting XML response.

Since it is very easy to use, and the getting started guide in the site is quite easy to understand, I wont spend so much time here. Just a few screens:


(Adding a wsdl to a project)


(The XML request message generated)


(After requesting a service, with the XML response message)

Now, just install, test and have fun =D.

domingo, 26 de agosto de 2007

Requirements

I just found this image around my messy HD.

(Click to enlarge)

It is funny because it's true.

quarta-feira, 15 de agosto de 2007

Cryptography with Caché/Ensemble

Some months ago I was looking for cryptography APIs within Caché/Ensemble, because I was going to implement some security at Web services (something simple, actually, very far from WS-Security - here are the specs, if you want).

After searching Caché´s and Ensemble´s online help, and asking the big G without success, I gave up. I finally realized that there were not any Caché API for it. Something curious, since even Oracle has one API for crytography operations.

Never mind. What mattered was finding a solution. My first idea was to use a Java Gateway. You might ask what is it? It is nothing more than a way to create a Java virtual machine inside Ensemble, and be able to interact with Java objects in it. Since Java has some good crytography API, I thought my problems were over... Unfortunately, this JVM/Caché partnership wasn´t very happy, with a lot of bugs and strange stuff (some crashes froze the machine, and I had to reboot it).

Then, I had two options. Or I would wait some years until someone decides some cryptography API would be nice and put it into Caché, or I would write something myself. I decided for the second option.

First question: which algorithm should I implement? My 'security architecture' was not very complex, so I discarded the public/private keys algorithms. Then, which symmetric algorithms I would choose? As I was not getting any raise or something, I wanted the most easy to implement (but not so easy to break, like Caesar´s).

Actually, I didn´t find any algorithm that was so easy. So I picked one that had good material/refereces available. I ended up with DES. I really don´t remember all pages where I researched, but two good references are: The DES Algorithm Illustrated and DES Encryption.

In the end, all code was distributed in 3 classes (although one of these classes is just a holder). I won´t post here the source code, since it is a lot of code, if you want to see it, you can download it here (some warning: comments are in portuguese, if you want some detail, leave a comment here in the blog).

The class DadosLoginBean is only used as holder to user/password.

The class Util has, besides specific methods for DES algorithm, several methods that can be used in other places, as methods to convert strings with ASCII to hexadecimal representation, or hexa values to bitstrings (bitstring is an special data structure, an array of bits, used in Caché).

The main class is DESCrypto. Since I´m tired of writing here, I will just say that this class ciphers/deciphers the data using DES algorithm, with block modifier CBC, and padding PKCS#5, with an initial vector of zeros.

You should not worry if you didn´t catch the letter soup above. It also took me some time until I figured out all those letters =P. Anyway, the last method of DESCrypto, decifraLogin, shows how to use all this stuff (if you just want to cipher/decipher using those classes, you really don´t need to know all the letter soup). The method decifraLogin has as first parameter one ciphered message (in hexadecimal as %String), and a key (also in hexadecimal as %String), and returns an object with user and password. Here is the code:


///
/// *******************************************************************
///
/// Static method to decipher a message with user/password.
/// It creates a DESCrypto object, sets the key, converts
/// the hexadecimal string to ASCII, and returns a bean with user
/// and password.
///
/// In case of any error, returns null (empty).
///
ClassMethod decifraLogin(msg As %String, chave As %String = "5C6F2F5F45793A50") As eey.security.bean.DadosLoginBean
{
set $ztrap = "EXCEPTION"

set des = ##class(eey.security.DESCrypto).%New()
set st = des.setKey(chave)
if ($System.Status.IsError(st)) goto EXCEPTION

set st = des.performChainCrypto(msg, .desc, "D")
if ($System.Status.IsError(st)) goto EXCEPTION

set userSenha = ##class(eey.security.Util).strHexToStringAscii(desc)

if (userSenha = "") goto EXCEPTION

set user = $piece(userSenha, "/")
set pass = $piece(userSenha, "/", 2)

set bean = ##class(eey.security.bean.DadosLoginBean).%New()
set bean.usuario = user
set bean.senha = pass

kill des

Quit bean
EXCEPTION
Quit ""
}


If you are interested, take a look at the source code, and leave a comment if you have any doubt.

P.S.: to those interested, above code is under WTFPL license. =P

quinta-feira, 26 de julho de 2007

Caché Operators

It's been a while since I found out something peculiar about Caché's language, Caché Object Script. It is not really a bug, since it's registered at the documentation, but I don't think it's very natural or straightforward.

If you remember your mathematic class when you were a child, you might give the correct answer for this expression: 1 + 2 * 3 (* means multiplication, if you don't already know =P). If your answer is 9, you probably skip some classes, since the correct answer is 7. That's because in math, multiplication operator has precedence over addition and subtraction operators (see this link). It means that if you don't put parentheses (which is the "right way" to express the order of operations, if you want), you must do multiplication before addition.

Well, most of programming languages I know, consider mathematics (what is absolutely normal, since science computer was born from maths, together with eletric engineerings).

Examples:
Oracle database, I can do: SELECT 1 + 2 * 3 FROM DUAL, and the answer will be 7. So Java, C/C++, Delphi/Pascal, gives us the correct answer, 7.

But with Caché Object Script, the answer is 9. Try open a terminal, and execute the following: WRITE 1 + 2 * 3, the answer will be 9.

This happens because this language doesn't follow mathematics pattern, neither most of language's. Fine, in fact, it's a matter of implementation, and it's in documentation, but I can't stop looking it badly. Even worse when I am used to the rest of world's convention, used to "right" operators precedence. Oh well, the language can choose if it will follow or not, any convention. But if you do not, especially math's conventions, you might cause some errors.

I just payed attention to this, because one loop of mine wasn't working. Being familiar with other programming languagens, I didn't think a simple expression could be wrong (the max boundary of my loop was the result of a expression like I showed). After check the rest of algorith twice, I was just blessed by a hunch, and after some tests and debugging, I realized that I should have put some parentheses in the expression. After doing that, I checked the documentation and it was really there: operators precedence is strictly left to right, when no parentheses are found.

So, I just start wondering why those language designers made it, why they made this choice? Did they want to be 'cool', not just like everyone else, or maybe were they rebels with or without a cause?

I believe it was not the case. I think it was just because the implementation of script's execution, which is easier to do when you don't consider math operators precedence. Laziness. Less effort law, the law that really moves this world, despite Newton's gravity =P.

Even being a script language is not really an excuse. Javascript is also an script. But it goes with the rest of the world. Just try document.write(1 + 2 * 3) and you'll get a nice 7.

u_u

The forgotten parameter

While searching the source code of %IO.ServerSocket to write the previous posts (about sockets and ServerSockets and jobs), I just found out something at least, curious.

The method ListenJob from %IO.ServerSocket has as 6th parameter, pForeground, a boolean value which default is zero (means false in Caché). The (poor and little) documentation of this method even cites this parameter. The name of the parameter might indicate us the idea of controlling if spawned jobs would run in background or not, or even if the own method would run in background or not.

After some tests, I realized that the behaviour of ListenJob method didn´t change, even if I changed this parameter. So I went to see and analyse the source code, and surprise: this parameter IS USED NOWHERE. Oh yes, it´s declared in method's formal description, and nowhere else.

Dumb, very dumb, I thought. But, I decided to give it a chance, and check some possibilities that might have led to this situation.

First theory: this class might be used in older versions of Caché, and to keep compatibility, they just left the method as it was. Theory status: not valid. In this docs of version 5.0, this class EVEN EXISTS.

Second theory: in some newer version, it might be used for something. Theory status: highly not probable. I don't have version 2007.1 here, so I can't see the sources, but as shown on this version's doc, I don't see any diferences at this class.

Last theory: someone just forgot the parameter there, left it alone and useless. Theory status: it's the theory I accept as true, for now.

Just to mention, I use Caché's version 5.2, therefore all links I post to documentation is for this version. So, in previous posts, all links go to 5.2, although in this case, all classes seemed the same, as I looked at 2007.1 docs.

There are things in this world... ¬¬

terça-feira, 24 de julho de 2007

More sockets in Caché

Returning to the subject of last post, about Caché's sockets, lets explore other possibilities.

At last post, I showed a simple example of how to create sockets, both server and client sockets. However, that example had a limitation: at the ServerSocket, the code shown only accepted one request, and after dealing with the connection, it ended its execution. As an example, it is still valid, but in a real-scenario, it's too limited.

In fact, the class %IO.ServerSocket has another method, which allows us to create a Server socket more alike a Java's ServerSocket. This method is ListenJob.

First, lets see a simple example code:


Class teste.ServerSpawnSocket Extends %RegisteredObject
{

ClassMethod server()
{
set sock = ##class(%IO.ServerSocket).%New()
set st = $$$OK

w "Starting server...", !

// open serverSocket, param:
// port, timeOut, status
w sock.Open(7777, -1, st), !
w st, !

// server to listen incoming connections,
// and when it arrives, spawns a new job in background
// params:
//
// timeOut,
// classNameContainingMethodToBeCalled,
// jobArguments,
// socketClassName (default: %IO.ServerSocket),
// maxNumberOfJobs (default: -1),
// runInForeground (default: 0 - no),
// status
w sock.ListenJob(-1, "teste.ServerSpawnSocket", "", , , , st), !
w st, !

w "Closing server...", !
quit
}

//===================================================

ClassMethod OnConnected(StreamHandler As %IO.DeviceStream, AnotherArgs)
{
set st = $$$OK

// reading from socket, param:
// maxStringSize, timeout, status, lineTerminator
set ^someVar("read from tcp") = StreamHandler.ReadLine(50, 30, st, $c(13))

// writing to socket, param:
// stringToBeWritten, automaticFlush, status
d StreamHandler.Write("Goodbye Dolly", 1, st)

d StreamHandler.Close(st)
set ^someVar("status") = st

quit
}
}


The method server() is quite simple, having more lines of comments than lines of code =P. After we create an object, again we call method Open. In this method, we define the port that the socket will keep listening. But the method we are more interested now, is the following one, ListenJob

This method does the following: when a new request arrives at the socket, if there's no restriction to request's address, the method creates a new job in background, passing as parameter an object of class %IO.DeviceStream, which holds the data stream created for the connection. But a question arises: what method does Caché calls? Well, after surfing through source-code of %IO.ServerSocket, I found out that after calling some internal methods of its own class, the object invokes a classmethod (in Java, we call it static class method) OnConnected, of the class whose name was passed as the second parameter of ListenJob.

ListenJob also can control the max number of concurrent jobs, which is defined in 5th parameter. The first parameter is our old known friend timeout, that in our example, is set to -1, to never expire. The third parameter might be made of a list, with parameters to be passed to OnConnected method. The 6th parameter is useless (I'll talk about it in the future), and the last parameter is also an old friend, an object with the status of method's execution.

At the example, we can see that we passed as parameter, the name of it's own class, but it could have been another class as well. The important is that the class has the class method OnConnected. The method's interface can be seen at the example. (IMPORTANT: this interface is not fixed or official, since I didn't find any reference to this in documentation, just in sources).

Inside OnConnected method, we can manipulate the data stream as we wish, the example just read and write something from/to the stream, before closing the connection.

That's it. Hope it is usefull.

segunda-feira, 23 de julho de 2007

Sockets and Caché

Some days ago, at Caché´s google groups, someone asked how to use sockets in Caché. I also have never used sockets directly in Caché, I thought it would be nice to find out how to. As I frequently say, Caché documentation is far from good docs, as Sun´s javadocs, therefore, a lot of things I discovered was through "feeling" and suppose-test-check cycles, although in this specific case, it was a bit easy.

Below, the example code I have posted in google groups. You can put this methods in any class, so you can test yourself:


ClassMethod connect()
{
set sock = ##class(%IO.Socket).%New()
set st = $$$OK

w "Starting", !

// connecting, param:
// address, port, timeout, status
w sock.Open("127.0.0.1","7777",-1, st), !
w st, !

// reading from socket, param:
// maxStringSize, timeout, status, lineTerminator
w sock.ReadLine(50, 30, st, $c(13)), !

// writing to socket, param:
// stringToBeWritten, automaticFlush, status
d sock.Write("We are the champions", 1, st)

quit
}

//===================================================

ClassMethod server()
{
set sock = ##class(%IO.ServerSocket).%New()
set st = $$$OK

w "Starting server...", !

// open serverSocket, param:
// port, timeOut, status
w sock.Open(7777, -1, st), !
w st, !

// server to listen incoming connections, param:
// timeOut, status
w sock.Listen(-1, st), !
w st, !

// writing to socket, param:
// stringToBeWritten, automaticFlush, status
d sock.Write("I am the champion", 1, st)

// reading from socket, param:
// maxStringSize, timeout, status, lineTerminator
w sock.ReadLine(50, 30, st, $c(13)), !

w sock.Close(), !

w "Closing server...", !
}


The first method, connect(), uses a %IO.Socket, which implements a basic client socket. If you are familiar to Java, you'll realize this class is very simple to use. Actually, after you create the object, you can just cal method Open, with the host´s address and port, a timeout (-1 at the example means no timeout at all) and a status object as parameters.

After connected, you can use methods from %IO.DeviceStream (which %IO.Socket inherits), to read/write data stream.

//===================================================

The second method show above, server(), uses a %IO.ServerSocket. The difference is that ServerSocket implements a server, i.e., a socket that keeps listening requests, and IO.Socket connects to a ServerSocket. Unlike Java´s ServerSocket, which returns a Socket object, %IO.ServerSocket can be used as an ordinary socket. We can see that the same methods are used to write/read to/from both socket objects.

It is also possible to keep the ServerSocket listening, and when a connection is attempted, start other job in background, to handle the new connection. I will explore this in some future post.

WTF is it about?

I am a person who doesn´t really like blogging. Unfortunatelly, some subjects I talk about at my other blog, in portuguese, could really interest a broader audience, since it seems there is a lack of research material on Caché.

So, I´ll be replicating here some of my posts. That´s it.