Upload
toby-collins
View
227
Download
0
Tags:
Embed Size (px)
Citation preview
Peter van DamPeter van Dam
DynamicDynamic WebClient WebClientProgrammingProgramming
Peter van DamPeter van Dam
Progress fanatic since 1985Version 3-4-5-6-7-8-9CHUI-GUI-Batch-WebSpeed-WebClientFounder of www.v9stuff.comAuthor in Progressions & user group
magazinesFounder of www.netsetup.nl
Dynamic WebClient ProgrammingDynamic WebClient Programming
Dynamic Windows, Frames & WidgetsDynamic BrowsesDynamic Buffers & QueriesDynamic Temp-TablesObjects, Handles, Methods & AttributesAppServersWebClient
What is WebClient?What is WebClient?
WebClient = runtime – db connect
WebClient = runtime – db connectWebClient = runtime – db connect
Need AppServer to talk to a databaseSeparate User Interface from Business
Logic
AppServerAppServer DBDB
Run Progress over inter/intranetRun Progress over inter/intranet
Full GUI capabilities on end user PCWebClient does NOT run in browser
AppServerAppServerDBDBInternetInternet
ororIntranetIntranet
Prepare your applicationPrepare your application
No database connects from the clientPort your application to AppServerUser Interface runs on clientBusiness Logic runs on serverClient issues AppServer callsADM II offers these capabilities
Consider the following windowConsider the following window
Window architectureWindow architecture
Built in ADM IISDO, SmartDataBrowser,
SmartDataViewer, SmartPanelRun with AppServerRowsToBatch=100Nothing fancy (we’ll get to that later)
Startup time 6 s (!) over 28k8Startup time 6 s (!) over 28k8
Startup statisticsStartup statistics
198 KB of client-side r-code to start6 AppServer calls over internet117 KB of server-side r-code to start19 KB of data received100 rows cached on client
After 100 rowsAfter 100 rows
4 AppServer calls18 KB of data received200 rows cached on clientResponse time 4 seconds on 28k8
DeploymentDeployment
This screen compiles to 198 KB of client-side r-code
Remember, nothing fancyNot a single bit of custom codeImagine deploying an entire
application…
Deploying an application (I)Deploying an application (I)
Consider an average application with 100 screens
Some screens are simplerMany screens are more complexSome screens are much more complexSome objects are being reused
Deploying an application (II)Deploying an application (II)
A conservative guess: average screen size 250 KB of client side code
100 x 250 KB = 25 MBNot counting images, adm tree, support
code etc. Download times:
28k8 modem: ~ 150 minsDual ISDN: ~ 34 mins
Deploying an application (III)Deploying an application (III)
Compressing .r code saves just 20 %Initial deployment on CD possible (with
WebClient image)However, even small application
updates turn into megabytes to deployDid we choose internet to limit
deployment possibilities?
Surely there is a better way?!?Surely there is a better way?!?
Three Golden Rules:Three Golden Rules:
1. Reduce AppServer calls
2. Reduce network traffic
3. Reduce client .r code
Consider this window again:Consider this window again:
What do we really really need?What do we really really need?
Apply the Golden RulesApply the Golden Rules
1. A single AppServer call should do
2. Do not send all viewer properties for all records
3. Create windows dynamically
A single AppServer call should doA single AppServer call should do
All data for the entire window should be prepared on the server and returned in a single call
The server needs to know about the screen configuration
Describe screen definitions in a database (‘Repository’)
Do not send unnecessary dataDo not send unnecessary data
The browser shows just 3 fields Fetch the viewer data only when
needed Activate a time delay when the
user scrolls The ideal call returns just a single
packet (up to about 1,500 bytes)
Do not send unnecessary data (2)Do not send unnecessary data (2)
Ask yourself: Does the client REALLY need this
information from the server? Do I send information to the
server that the server already knows?
Do not send unnecessary data (3)Do not send unnecessary data (3)
The browser shows just 3 fields Fetch the viewer data only when
needed Activate a time delay when the
user scrolls A call should ideally return just a
single packet (1500 bytes)
3: Create windows dynamically3: Create windows dynamically
Send repository data to the client Client creates all screens
dynamically NO r-code deployment at all! In V9.1 this is NOT fiction
New startup statisticsNew startup statistics
Startup time 2 seconds on 28k8Client code always resides in memoryA single AppServer call41 KB of server-side r-code to start6 KB of data received100 rows cached on client
Why do we show the first 100?Why do we show the first 100?
The example window shows the first 100 customers
What are the chances that this is useful?
The first thing the user probably will do is find the customer she needs
Why start with a useless data transfer?
A new approach: start emptyA new approach: start empty
Let the user enter her selections first
A new approach: start emptyA new approach: start empty
Transfer only what the user asks for
Results for the new Results for the new approachapproach
1 second response time on 28k8 for each user interaction
Just 2-3 KB received on each callThe application is now usable with a
28K8 modem and simply fast on anything better
9600 baud9600 baudDemonstrationDemonstration
How does it all work?How does it all work?
Write a generic ‘rendering’ program for the client (ui.p)
Introduce a repositoryWrite a generic program on the server
that can merge repository information with data
The Big PictureThe Big Picture
Appserverboundary
RepositoryRepository
ui.p
ApplicationApplicationdatadata
bl.p
WebClient AppServer
ApplicationApplicationdatadata
uihooks.p blhooks.p
The implementationThe implementation
Appserverboundary
RepositoryRepository
ui.p
ApplicationApplicationdatadata
save.p
ApplicationApplicationdatadata
Delete.p
getscreen.p
Creates:Dynamic windows and dialogsDynamic field level widgetsDynamic panelsDynamic browsersAnd fills them with dynamic data from the server
ui.p
Fetches screen description from repository
Creates dynamic database queries for each data set
Puts the results in dynamic temp-tablesReturns dynamic data and static screen
description to client
getscreen.p
Dynamic Temp-TablesDynamic Temp-Tables
New in Progress v9.1Created specifically for dynamically
passing data from an AppServer to a WebClient
Create temp-table structures on the flyRequires only run-time Progress
Dynamic Temp-Tables (2)Dynamic Temp-Tables (2)
Move data from getscreen.p to ui.p without knowing about db tables at compile time
Create in getscreen.p at runtimePass definitions & data to ui.p using
new OUTPUT TABLE-HANDLE parameter
Creating a Dynamic Temp-TableCreating a Dynamic Temp-Table
DEFINE VARIABLE hTemp as HANDLE NO-UNDOCREATE TEMP-TABLE hTempASSIGN hTemp:UNDO = FALSE.
Just like any dynamic widgetNow we have created an empty
structureUse methods to define the structure
Dynamic Temp-Table MethodsDynamic Temp-Table Methods
CREATE-LIKEADD-FIELDS-FROM
ADD-NEW-FIELDADD-LIKE-FIELD
ADD-LIKE-INDEXADD-NEW-INDEXADD-INDEX-FIELD
Use Use ADD-NEW-FIELDADD-NEW-FIELD
Most versatileWe may want to overrule some
dictionary properties in the repositoryWe want to add some extra fields such
as the database ROWID of a recordCheck the return value for errors
ADD-NEW-FIELDADD-NEW-FIELD
ADD-NEW-FIELD( field-name-exp,
datatype-exp
[ , extent-exp
[ , format-exp
[ , initial-exp
[ , label-exp
[ , column-label-exp ] ] ] ] ] )
ADD-NEW-FIELD ADD-NEW-FIELD examples examples hTemp:ADD-NEW-FIELD ("rowid","rowid”).
hTemp:ADD-NEW-FIELD (hField:NAME, /* from dd */ hField:DATA-TYPE, /* from dd */ hField:EXTENT, /* from dd */
repository.cFormat, repository.cInitial, repository.cSideLabel repository.cColumnLabel).
IndexesIndexes
We do not need any indexes on these temp-tables
The client shows the records in the order in which they were created
Any server-side sorting includedClient may sort the records locally as
well
Prepare the Temp-TablePrepare the Temp-Table
hTemp:TEMP-TABLE-PREPARE(<name>). ‘Compiles’ the temp-table at run timeCheck the return value for errorsProvide the name argument for
debugging purposesNow you can fill the new structure with
data… dynamically
Populate the Dynamic Temp-TablePopulate the Dynamic Temp-Table
We need a buffer handle for this:
DEF VAR hDefault AS HANDLE NO-UNDO.
hDefault = hTemp:DEFAULT-BUFFER-HANDLE.
But how do we get the data into the
dynamic temp-table?
Populate the dynamic temp-tablePopulate the dynamic temp-table
First we need to create a dynamic buffer for each database table involved in the query:
DEF VAR cTable AS CHAR INITIAL “customer”.
DEF VAR hData AS HANDLE NO-UNDO.
CREATE BUFFER hData for table cTable.
Populate the dynamic temp-tablePopulate the dynamic temp-table
Then we create a dynamic query:
DEF VAR hQuery AS HANDLE NO-UNDO.
CREATE QUERY hQuery.
hQuery:ADD-BUFFER(hData).
hQuery:QUERY-PREPARE(SUBSTITUTE(
“FOR EACH &1 NO-LOCK”, cTable)).
Populate the dynamic temp-tablePopulate the dynamic temp-table
Then we open the query and buffer-copy the data:
hQuery:QUERY-OPEN().hQuery:GET-FIRST().DO WHILE hQuery:QUERY-OFF-END = FALSE: hDefault:BUFFER-CREATE(). hDefault:BUFFER-COPY(hData). hQuery:GET-NEXT().END.
Returning the dynamic temp-tableReturning the dynamic temp-table
Don’t forget to fill the ROWID field as well Maximize the number of records to be returned
(e.g. 100 or even 50) The new dynamic temp-table (meta schema +
data) is returned to the client using OUTPUT PARAMETER TABLE-HANDLE
DELETE OBJECT hTemp. (never forget!) Delete magically postponed by Progress
OUTPUT TABLE-HANDLEOUTPUT TABLE-HANDLE
RepositoryRepository
ui.p
ApplicationApplicationdatadataApplicationApplication
datadata
getscreen.p
11
22Appserverboundary
3 OUTPUT TABLE-HANDLE3 OUTPUT TABLE-HANDLE
Receiving the dynamic temp-tableReceiving the dynamic temp-table
The dynamic temp-table is received by the client using
OUTPUT TABLE-HANDLE
Client must analyze meta schema + dataHow the heck do we go about this?
Receiving the dynamic temp-tableReceiving the dynamic temp-table
First analyze the meta schema:DEF VAR hData AS HANDLE NO-UNDO.
DEF VAR iField AS INT NO-UNDO.
DEF VAR hField AS HANDLE NO-UNDO.
hData = hTemp:DEFAULT-BUFFER-HANDLE.
DO iField = 1 TO hData:NUM-FIELDS:
hField = hData:BUFFER-FIELD(iField).
/* Access field properties here */
END.
Analyzing the meta schemaAnalyzing the meta schema
By inspecting the buffer fields we can find out meta information
Note that the client receives a virtual tableMeta information includes data-type,
format and labelsOther information such as row and
column needs to be sent separately
Analyzing the meta schemaAnalyzing the meta schema
The client can use the meta information creatively to render the user interface
Could even be tailored individually The server does not care about the user
interfaceHowever, the server DOES care about
the data and the relationships within it
Analyzing the dataAnalyzing the data
Looks familiar already:
DEF VAR hQuery AS HANDLE NO-UNDO.
CREATE QUERY hQuery.
hQuery:SET-BUFFERS(hData). /* Just 1 */
Now open the query and fetch the records A viewer should return a single record, a
browser may return many
Record scopingRecord scopingDynamic BUFFERS have no scope –
they are created in the sessionUse a WIDGET-POOL to scope to your
procedureYou are responsible for releasing the
record to write it to the databaseThen you must delete the buffer object
Record scopingRecord scoping
DEF VAR hBuffer AS HANDLE NO-UNDO.
CREATE BUFFER hBuffer for table ‘customer’.
DO TRANSACTION:
hBuffer:BUFFER-CREATE().
/* fill here */
hBuffer:BUFFER-RELEASE().
END. /* Transaction */
Memory leaksMemory leaks
Show up after a long timeCan be extremely difficult
to track down
Bites you at
deployment!
Memory leaksMemory leaks
Dynamic objects arescoped to the session
You are responsible fordeleting objects when you are done with them:
DELETE BUFFER hBuffer.hBuffer = ?.
Memory leaksMemory leaks
Use WIDGET-POOLS toscope objects to yourprocedure
TABLE-HANDLES are created in the SESSION WIDGET-POOL (whether you like it or not) on both sides
You need to delete those explicitly
TABLE-HANDLESTABLE-HANDLES
DEFINE OUTPUT PARAMETER
TABLE-HANDLE hTemp.
/* Creates the table *//* Fill hTemp with data */DELETE OBJECT hTemp./* Will be deferred by Progress until procedure goes out of scope */
Dynamic Temp-Table ViewerDynamic Temp-Table ViewerHelps you troubleshoot
dynamic temp-table memory management problems
One ADM II issue shown here
Download tool from www.v9stuff.com
Advantages of dynamic programmingAdvantages of dynamic programming
Eliminate CRC errors Create extremely powerful code No more .r code to deploy Even hardly any .r code on the server Faster applications Easier multi-database programming Run Progress applications across the
internet
Drawbacks of dynamic programmingDrawbacks of dynamic programming
New paradigm to learn More abstract, work with handles Difficult to debug Repository replaces code Different deployment issues More runtime errors Possible query performance problems
More informationMore information Progress documentation:
– V9 Product Update Bulletin– Progress on the Web
www.v9stuff.com Progressions magazine (www.wss.com) Walvis Database Viewer Progress Email Group (www.peg.com)
Question timeQuestion time
[email protected]@netsetup.nl
WebClient & WebSpeed consultantsWebClient & WebSpeed consultants
Progress Solutions for InternetProgress Solutions for Internet