Robert Cemper · Feb 6, 2019 go to post

In addition to the reply from @Evgeny Shvarov  :

INT is highly important to identify error locations.
eg. <DIVIDE>label+5^routinename refers to the location in routinename.INT  

Robert Cemper · Feb 5, 2019 go to post

Out of curiosity, I have built a  more exotic solution that is easier to understand. (At least for me)
I don't like so much this protocol upgrade stuff and encrypting and simulation of a browser.

My personal workaround:

  • start browser over ZF(-1, "start chrome http://.................my csp......")
  • the page gets my request passed with the URL. very simple just as a Hash
  • the page does all the WS stuff via JavaScript
  • the reply is returned using Hyperevent #call(....) 

It works fine and is rather "classic CSP" style.

Robert Cemper · Feb 5, 2019 go to post

OK.

I tried it and found:  sock.Open(  does not accept "/ws/v2/?&token=<token>"  only the server name

USER>set sock = ##class(%IO.Socket).%New()
USER>do sock.Open("localhost/csp/samples/Web.SocketTest.cls",8080,5,.sc)  //// >>> Timeout
/// but this works
USER>do sock.Open("10.10.12.87",57772,5,.sc) zw sc
sc=1
USER>zw sock
sock=<OBJECT REFERENCE>[2@%IO.Socket]
+----------------- general information ---------------
|      oref value: 2
|      class name: %IO.Socket
| reference count: 2
+----------------- attribute values ------------------
|        (%Attached) = ""
|(%CurrLineTerminator) = ""
|              AtEnd = 0
|       CharEncoding = "Native"  <Set>
|               Host = "10.10.12.87"
|    InputBufferSize = 32767
|             IsOpen = 1  <Get>
|       IsSingleByte = ""
|  KeepAliveInterval = 0
|     LineTerminator = $c(10)
|     LocalInterface = ""
|               Name = "|TCP|57772|20242"
|   OutputBufferSize = 32767
|               Port = 57772
|             Remote = "2019-02-05 10:33:35.212|10.10.12.87:57772"
|          SSLConfig = ""
|   TCPReceiveBuffer = 0
|      TCPSendBuffer = 0
|   TranslationTable = "RAW"  <Set>
+----------------- swizzled references ---------------
|i%DisconnectHandler = ""
|r%DisconnectHandler = ""
+--------------- calculated references ---------------
|DefaultFlushOnWrite   <Get>
|        IsCharacter   <Get>
+-----------------------------------------------------
USER>

Now you have the connection BUT no server to take care of it as you see:

USER>do sock.WriteLine("/csp/samples/Web.SocketTest.cls",1,.sc) zw sc
sc=1
USER>write sock.ReadAny(32000,5,.rSC) zw rSC
HTTP/0.9 404 Stream Not Found
Content-Type: text/html; charset=utf-8
Date: Tue, 05 Feb 2019 10:45:16 GMT
Expires: Tue, 05 Feb 2019 11:45:16 GMT
rSC=1


Investigating the browser it became clear what's going on:
- you begin with HTTP to start your WebSocketServer
- an then initiate the WS connection.

This is the output from TcpTrace: 

>>>>>  GET /csp/samples/websocketdemo.csp HTTP/1.1 >> .......

>>>>>  GET /csp/samples/Web.SocketTest.cls HTTP/1.1  >> ... >> Upgrade: websocket

That's my point:
      With the actual approach starting the requres WS Server is missing. 

Robert Cemper · Feb 4, 2019 go to post

To me, this looks like your port doesn't like WS as initial connection protocol but expect the switch from HTTPS -> WSS or the port is just wrong.

several suggestions for the investigation to get it moving:

#1) verify your server from a normal web page (e.g. based websocketdem.csp in Samples) especially the port !!!!

#2) If you have control over your server then skip SSL and get running over HTTP -> WS first. You can add this once it works.

#3) If you have no server just use Caché / SAMPLES /  Web.SocketTest.cls 
I found it very useful to have control over both ends and now I own several variants for testing.

Staying tuned yes

Robert Cemper · Feb 4, 2019 go to post

YES! you can!

Just extend the method (just some example)

ClassMethod exmple(inout As %StringAs %String SqlName ExampleSqlProc ]
{
  do prepare^myMumps(inout)

  set return=$$return^myMUMPS()
 ;;; or whatever is required
   return "Echo:"_return 
}
 
Robert Cemper · Feb 4, 2019 go to post

#1) if it is a Caché Backup you need an Installation of Caché to restore it.

#2) if it is a CACHE.DAT file you also need an installation of Caché and mount this as an additional DataBase.

Without the installation of Caché it's a thrilling exercise to high-level experts. Not really advisable.

try to have the same processor type { big / little endian :== (Intel/AMD) or not } as the original Caché instances. This saves some headache.

Forget about SQL Server. Caché is far more  efficient and advanced technology!

Robert Cemper · Feb 4, 2019 go to post

 #1)

You need to get %Net.HttpRequest in hands to set your property Content Type.

#2)

instead of PutURL(...) you have to use method SendFormDataURL(....)  of EnsLib.HTTP.OutboundAdapter

more details in docs here:

Creating Custom HTTP Requests

If you use the more common methods of the HTTP outbound adapter (such as Get), the adapter automatically creates and sends an HTTP request, which can include either form data or a request body. In special cases, you may want to create a custom HTTP request so that you can specify details such as proxy authorization or a different character encoding.
Robert Cemper · Jan 27, 2019 go to post

at first sight, I miss in this  piece  of code

ClassMethod GetPage() As %String) [ ZenMethod ]
{ quit 
%session.Data("currPage") }

and

var pageNo zenPage.GetPage() ;

Robert Cemper · Jan 26, 2019 go to post

I'd personally prefer that $LB(var) also follows the elementary rules of COS.
Though I have no hope on any ProdLog on this subject

Robert Cemper · Jan 26, 2019 go to post

as pointed out by Alexander Koblov  already:

you can create a $LB() od $LB(undefined) and it's all the same

set a=$LB() zzdump a
0000: 01

or 

write $d(undefined)

0

set a=$lb(undefined) zzdump a

0000: 01

Your coincidence of NULL, null, Null, nuLL,  .... is just an optical eye-catcher without syntactical relevance in COS

Robert Cemper · Jan 24, 2019 go to post

You could use the feature that any Classmethod may also serve as Stored Procedure.

Like this:

Class User.Remote
{
ClassMethod Echo(inout As %String) As %String [ SqlName = Demo, SqlProc ]
return "Echo:"_inout }
}

And then you may call your Procedure like this: getting back

SELECT DEMO('hello WORLD') 

getting back   

Echo:hello WORLD

All you have to care for is to return something.
what happens inside your ClassMethod is up to you and doesn't need to be related.

Robert Cemper · Jan 21, 2019 go to post

As you have an installation of Caché you will also have the Documentation with it.

I recommend "Using Caché ObjectScript" to start with ObjectScript.
It is also public accessible https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCOS

I also recommend having a look at the free online training library.
The link is in the header of this forum:  LEARNING

Browse Catalog and search for O bjectScript Basics

All details on individual commands, functions, system variables are again in your local instance or in the
public reference on docs.intersystems.com

Robert Cemper · Jan 20, 2019 go to post

My personal preference is to a have a solution NOW.
And not wait for something that may take too long for me.

just the most simple example to solve your issue with a ClassMethod:

Class nodes.Select
{
ClassMethod Address1(town As %String = "") As %String
{ set (list,id)=""
    for  set id=$o(^Customer(id)) quit:id=""  
            if ^(id,"Address",1)=town set list=list_$lb(id) }
   return "["_$lts(list)_"]"
}}
Robert Cemper · Jan 20, 2019 go to post

See details in NoSQL Methods for Global Nodes

Especially  Get the Next Global Subscript: next() lets you iterate over first subscript  ^Customer(id)   1, 2, 3, ...

next using Retrieve a Global Node: get() you access ^Customer(id, "Address", 1) and check if it is "London"

from docs mydata.retrieve 

This method will return a list of subscript values that are defined directly beneath a given level in the global.

In your call, you get the full global. but you have to do the detail work yourself.

There is no such thing as SELECT ...... FROM GLOBAL WHERE .......

This is NoSQL.

But you may hide your functionality in a ClassMethod inCaché and use invoke_classmethod()

Robert Cemper · Jan 18, 2019 go to post

Hi Julius,
You got my point!
A Stream is a Stream and a String is a String and
they are different things with different handling. 
The fact that they both (can) contain characters is of no importance. 

Robert Cemper · Jan 17, 2019 go to post

Without offering a solution to develop this magic data type that Julius suggested
You should also define what datatype you plan to present for the SQL side.
And that is always taken from the Compiled Property definition.

All streams typically present themselves a CLOB or BLOB or similar and have no MAXsize
While a String presents itself as a VARCHAR with a MAXSIZE.
I see no way to manipulate this on the fly.

For object access, you may write a Setter and Getter that covers the real nature of your data:
For SQL access I see no chance at the moment.

Indexing is another issue. This would require another piece of magic.

My suggestion:
have a %String
have a %Stream
and have a calculated property of  %String to decide which one to present.

like a centipede with a wooden leg: 99 times tic and 1 toc

the stream is then truncated and still requires extra coding. 
 

Robert Cemper · Jan 14, 2019 go to post

Thanks! That solves my questions.
I just see close to me a situation that the upgrade to a higher version ( 16.2 to 18.1)
may trigger quite an effort on updating all developer's Studio. With all that "can never happen" devillaugh

Robert Cemper · Jan 14, 2019 go to post

Hi John,

Would this include a certain level of independence between editing and Caché, Ensemble, IRIS version?
Especially a kind of "forward" compatibility as long as you don't go for new features?

Robert Cemper · Jan 14, 2019 go to post

Did you check it with Chrome or just the default browser ?

for similar reasons I have set my default browser to Chrome

Robert Cemper · Jan 11, 2019 go to post

I'm glad I could help the community with my answers and
I especially like to express my BIG THANK to my readers voting for me.
This clear and positive feedback is a strong motivation for me for the future years.   

Robert Cemper · Jan 10, 2019 go to post

My interpretation of th eerror message is:
The JDBCdriver gets a request that it doesn't understand for a some reason but doesnt uncover the details.
The issue is to my understanding sowher in the Java part outside Caché.
I'd suggest to contact WRC as the know how to trace this and also understand the results.

Robert Cemper · Jan 7, 2019 go to post

You are right: LNX distrib + Win Distrib were the same build.
As far I remember from the version string  "2018.1.1.643.0 "  only "2018.1.1" is important.
And also the container should have $ZV to verify  (No idea what's in there actually )

Robert Cemper · Jan 7, 2019 go to post

Hi Wolf,

I ran into a similar problem a few weeks ago: IRIS on Linux - no studio

My solution: Custom install on my local WIN10 desktop. Only the Studio (and ODBC drivers).
Nice side effect: the new IRIS-Studio also talks seamlessly to my local Caché. 

My assumption: As it works for an isolated Linux it should work for Docker as well (if no firewall blocks you wink)

Robert Cemper · Jan 4, 2019 go to post

HIVE docs on  String Types shows me:

and

Varchar

Varchar types are created with a length specifier (between 1 and 65535), which defines the maximum number of characters allowed in the character string. If a string value being converted/assigned to a varchar value exceeds the length specifier, the string is silently truncated. Character length is determined by the number of code points contained by the character string.

Related to your  ERROR that tells us that STRING has no size limit => it is a STREAM in our terms.
 

So if you don't convert a STRING to VARCHAR   [ preferable VARCHAR(255) ] you won't be able to use an alphanumeric ID
You may, of course,  add some artificial numbering of type BIGINT to be used as ID.

In any case, just with data type STRING I'd call this a rather a text file than an SQL usable table.

Without touching the original source you may need to write your own loader:

  • reading the HIVE "table" sequentially row by row
  • insert it into a manually designed table/class with automatic id
Robert Cemper · Jan 3, 2019 go to post

Using UUID is a .save way to have a UNIQUE Key.
And Open by Key is a stable way for access. 

BUT be warned: replacing the default ID means locking you out from Bitmap indexing.
This might not be an issue with modestly sized tables. 
With larger ones it may kill your query performance

Robert Cemper · Jan 2, 2019 go to post

Jour problem is that morder**....js is not found in your CSP library structures.

Better start with the official example in namespace SAMPLES:  Class Web.SocketTest Extends %CSP.WebSocket

That one is really useful to start