You got me there

Working with CONDCHECK I got confused. The condition check works to mark a field that is filled out incorrectly, but the condition is to be read as “these conditions should be satisfied, if not…”.

Which makes sense; you write the condition that you want satisfied. If it isn’t you get the message.

My problem was that I thought of it in the context of if … then; if this condition is satisfied, I should get the message. Which I now realise was wrong.

Advertisements

An XSLT example

I needed this, so here it is for posterity:

In the list BUTLST, I wanted to select the value from the field WCHAR36, on the entry where the value from the field BUTNR matched the mapped field IZBUTNR.

So, in sequence, I wanted

the list BUTLST

/lxml:data/lxml:lists/lxml:list[@name='BUTLST'] ...

the value from the field WCHAR36

... lxml:column[@name='WCHAR36']

on the entry where the value from the field BUTNR

... lxml:list-entries/lxml:entry/lxml:column[@name='BUTNR'][text() ... ]

matched the mapped field IZBUTNR

... = key('field-value', 'IZBUTNR') ...

Completed that is

/lxml:data/lxml:lists/lxml:list[@name='BUTLST']/lxml:list-entries/lxml:entry/lxml:column[@name='BUTNR'][text() = key('field-value', 'IZBUTNR')]/../lxml:column[@name='WCHAR36']

with

Def_List Name(#BUTLST) Fields(#BUTNR #BUNAVN #WCHAR36) Type(*WORKING) Entrys(*MAX)
Web_Map For(*BOTH) Fields(#IZBUTNR #BUTLST )

 

8 characters is the limit

I keep forgetting this, cause I do development in other languages and frameworks as well, and nothing in the LANSA editor stops me. But WAM command handlers must not have a name of more than 8 characters.

Or rather, it can and it will work, but then when you try to set it up in the VLF, you, again without warning, enter the more-than-8 character name and lo and behold, you end up with errors on your site.

Instead, a command handler named, for example, CH_FAKKOP, should be referenced in the VLF by the first 8 characters, CH_FAKKO, even though the WAM component is 9 characters.


Edit: This also applies to the WAM name in XSLT, like

<xsl:with-param name="on_change_wrname" select="'UHandleEvent'" />
<xsl:with-param name="on_change_wamname" select="'CH_FAKKO'" />

Decimal points and functions

This is one of those issues that arise from me coming from a different background than what I imagine a typical Lansa developer, indeed a typical i series developer, come from. It is a useful reminder what decimals actually mean.

For example, I have a repository field that is defined like this:

Define Field(#JSN) Type(*SIGNED) Length(4) Decimals(2)

That means, a signed decimal value with 2 digits before the decimal point and 2 digits after the decimal point. In a function, I calculate the value and exchange it back to application calling. In that application, I (unfortunately) have an override, changing the number of decimals.

Override Field(#JSN) Decimals(1)

This was probably done to format the display of the value but where I imagined the value would be passed in a lower level format and then parsed in relation to the definition, this is not what happens.

Instead, the value is still the same 4 digits, but one of those that was after the decimal point is now before the decimal point. The value has been multiplied by 10^1; a value of 0.21 is now 2.1.

Because the override only moved the decimal point.

Newlines in Lansa 12 and 14

As someone mentioned a while back on the forum, getting a newline, or indeed any character from a character code, is difficult to find. But it is easy, once you know how:

(10).AsUnicodeString

returns a carriage return in ANSI. I’d probably prefer

(13).AsUnicodeString + (10).AsUnicodeString

as I am primarily targeting the Windows platform.

However, this is a solution for Lansa v. 14, not v. 12, where AsUnicodeString does not exist. In Lansa v. 12 you write

(13).AsByte.AsString + (10).AsByte.AsString

 

 

About password entropy

A quick follow-up on .IsValidPassword: The highest entropy of a password that would be accepted by .IsValidPassword(strong) can be calculated like this:

8 characters:
1 upper - 26 characters
1 lower - 26 characters
1 numeric - 10 characters
1 special - 7 characters
4 remaining - 26+26+10+7+1(space) = 70 characters

log2(26+26+10+7)*4+log2(26)*2+log2(10)+log2(7) ~~ 40 bits of entropy

If the requirement was simply 8 characters, the entropy could be

log2(70)*8 ~~ 49 bits of entropy

Entropy is used to calculate how long it would take to crack a password, provided it wasn’t an already know password:

Number of guesses = 2^(entropy - 1)

If we can make 1,500 guesses per second, divide by (1500 * 86400) (86400 is the number of seconds per day):

40 bits of entropy ~ 4,000 days
49 bits of entropy ~ 2.1 million days

What about words? Using words doesn’t necessary increase entropy, as words are part of a finite dictionary. The complete dictionary is somewhere between 100,000 and 200,000 but most people only use 3,000 words.

log2(3000)*4 ~~ 46 bits of entropy ~ 271,000 days
log2(3000)*5 ~~ 58 bits of entropy ~ 1.1 billion days

Using 4 words from your own dictionary would give a stronger password than what .IsValidPassword(strong) accepts, even if those 4 words would not get accepted by that method.

Password strength

I am not impressed with the Lansa v. 14 SP2 addition of .IsValidPassword (see documentation):

passwords-are-you-kidding-me

I am sorry. This is completely insufficient and wrong. Also, this is no secret as everyone knows that these rules are insufficient and wrong. Or should know.

So, what are the right rules?

  • Long passwords – this equals greater entropy
  • Use words that can be remembered – this way the password doesn’t have to be written down or stored digitally
  • Do not require use of mixed case and/or special characters – as this lowers entropy

Server modules

Did I mention that the Lansa documentation is incomplete?

Problem: Create a simple server module to accept a bit of data and simply reply with a string. Let that data be set with querystring parameters.

If I wanted to send the data as parameters in a POST request I would just do this:

POST to http://[DOMAIN/BASEPATH]/w=[WEBROUTINE]&r=[SRVROUTINE]&vlweb=1&part=[PARTITION]&lang=[LANGUAGE]

with, for instance,

FIELD1=DATA1&FIELD2=DATA2

But if the client expects to be able to send the data as part of the querystring, in a GET request, I need to do this:

GET to http://[DOMAIN/BASEPATH]/w=[WEBROUTINE]&r=[SRVROUTINE]&vlweb=1&part=[PARTITION]&lang=[LANGUAGE]&f(FIELD1)=DATA1&f(FIELD2)=DATA2

I could not find that f(…) trick, putting the parameters inside the parentheses, in the documentation but a very lucky search on the forum helped me.

My sample server module looks like this:

Begin_Com Role(*EXTENDS #PRIM_SRVM)

Define Field(#PROC_OUTPUT) Type(*string) Length(65535)

Srvroutine Name(DATA) Response(#RESPONSE)
  Field_Map For(*INPUT) Field(#FIELD1)
  Field_Map For(*INPUT) Field(#FIELD2)
  * do something with FIELD1 and FIELD2 here
  #PROC_OUTPUT := "Done"
  #RESPONSE.ContentString := #PROC_OUTPUT
Endroutine

End_Com

 

 

Trigonomic functions; also, precedence

Surprise! I am no friend of the Lansa documentation, so I thought I would add a few notes here.

I am grateful that there is an intrinsic function for floats as ArcTangent2, but which parameter is X and which is Y? The documentation is pretty sure that it is like this

#X.ArcTangent2( #Y )           /* not exactly... */

but I find that the parameters are reversed, if you take the thinking from elsewhere, like my previous programming experiences and for instance Wikipedia. A better explanation in the documentation could have helped me here, but I managed to find out that the problem was here. So I changed my code to

#Y.ArcTangent2( #X )           /* now similar to atan2(y,x) elsewhere */

Then comes the question of precedence… The difference between these two lines is obvious:

#A1 := (#DT / 2).Sine * (#DT / 2).Sine + #RT1.Cosine * #RT2.Cosine * (#DN / 2).Sine * (#DN / 2).Sine
#A2 := ((#DT / 2).Sine * (#DT / 2).Sine) + (#RT1.Cosine * #RT2.Cosine * (#DN / 2).Sine * (#DN / 2).Sine)

But I was surprised that they returned different results. I expected that both returned the same as the second one. Instead, the first returned the same as

#A3 := ((#DT / 2).Sine * (#DT / 2).Sine) + #RT1.Cosine) * (#RT2.Cosine * (#DN / 2).Sine * (#DN / 2).Sine)

I realize now that 1 + 2 * 3 in RDML(X) equals 9, whereas I would have expected 7 from my experience in many other languages. Now I know.