Web-Enabling Legacy Applications Jeff Haym Data Access Worldwide Consulting.
-
Upload
primrose-perkins -
Category
Documents
-
view
213 -
download
1
Transcript of Web-Enabling Legacy Applications Jeff Haym Data Access Worldwide Consulting.
Web-Enabling Legacy Applications
Jeff Haym Data Access Worldwide Consulting
Legacy Applications
Found in corporate environments and even in today’s vertical markets.
Generally procedural code.Hundreds of programs in application.Years to write and test.Modified FMAC or DFRun.Years of data entry means lots of data.
Keeping Legacy Apps
Users resist change. Application is stable and has earned trust and
confidence from users. Reputation is on the line and rewriting and deploying
might jeopardize that. If we have invested a lot of time and money and a
total re-write is not feasible. If we don’t want to re-train our users nor rewrite
training manuals. We can continue to use legacy apps to their full
capacity without modifying a line of code and have it work with WebApp server modules.
Opportunities For Those Who Maintain A Legacy App
Break free from legacy technology get into ASP / VB Script / Java Script programming.
Moving procedural app to object oriented is quicker with WAS as understanding UI system messages not necessary.
Knowing business rules and using ddb makes it easier to learn and understand DD methods.
User feedback puts us ahead of the game – less prototyping.
Optimal solution for remote access - Client / Server. Reuse rules on other back ends without re-writing
server side stored procedures. HTML is similar to procedural DataFlex. Get the RAD development for the web as you have
come to expect from DAW products.
Add Web Based Functionality One Module At A Time
Publish reports on the webEnable Data entryRemote access via the webWebApp programs are fully compatible
with 2.3 runtime and up. (File structure changes performed by dbb will upgrade file to Rev. 3 and 2.3 runtime will not work)
DD’s Strength
Hooks that can be programmed to allow for ‘any situation’ (mature methodology)
Consistently enforce these rules in every transaction (save edit and delete)
Ensure transaction integrity for multiple file saves in a DD structure.
Are DD’s Required? Although they automate so much work, and we
recommend using them, they are not required. However, it is best to let the dbb create them just
for the sake of running the wizards to generate ASP code with forms, that work with Web Business Objects, even if you do not initially use them to enforce business rules.
Modify wizard generated code to add procedural style reports, procedural style saves / edits / deletes.
New Workspace / Empty DDs
Creating a new workspace will keep new development isolated from old.
Copy data file components into W/S data directory. - When ready to go “live” switch DataPath in WebApp workspace to look at “live” data.
GRADUALLY move business rules to DDs, 1 by 1 as needed by web based modules.
Reporting - Probably The Best Place To Start.
Let anyone anywhere see data.Data is already stored and that is the
most valuable part of any system.Can use familial looping techniques, and
use WriteHTML to output data. (repeat … until, while [found]… end)
Embed HTML into values being written.
Procedural Report Example (ASP)
<% oVote1.call "msg_ListIdeas", TopicID%>
Procedural Report (WBO) Procedure ListIDeas integer iTopicID integer iCount string sCountURL sCount for_all thread by index.2 constrain thread.topicid eq iTopicID do
increment iCount move iCount to sCount move ("Vote1Zoom.asp" - "?RecId=" - (string(thread.recnum)))
to sCountURL get HTMLLink sCountURL sCount to sCountURL send writehtml sCountURL send writehtmlbreak (' ' + thread.title ) end_for_all End_Procedure
Send RegisterInterface msg_ListIdeas "msg_ListIdeas" "iTopicID" "Lists and numbers topic ideas"
Procedural Save (ASP)<% UserName = request.cookies("User_Name") %><% TopicID = request.cookies("TopicID") %><% CastVote = request("T1") %>
<%If request("Request_method")="POST" Then if Request("SubmitButton")<>"" then oVote1.call "msg_SubmitVote", CastVote,
UserName, TopicID end if end if%>
Procedure SubmitVote integer iVote string sUserName integer iTopicID integer iCount string sCountURL sCount clear progress move iTopicID to progress.topicid move sUsername to progress.user_name find eq progress by index.1 [found] begin if (progress.voted gt 0) begin send writehtmlbreak '' send writehtml "Current User Has Already Voted" procedure_return end
end for_all thread by index.2
constrain thread.topicid eq iTopicID do increment iCount
if iVote eq iCount begin // found the one that was voted for reread increment thread.votes saverecord thread unlock end end_for_all End_Procedure
Migrating To DDO’sWe do not want to initially move business
rules contained in 100’s of programs at once to DD’s.
Allow time to test the business rules.Even though DDs are the most
manageable to development and maintain business rules, we do not want to leap before looking.
Tips for Creating Data Dictionaries
Different runtimes enforce relationships differently. WAS / VDF runtime will check: relating field is same
size and type. Uniquely indexed parent. Child relates to parent, n : 1 Remove multiple relationships between two files.
Start by analyzing relationships first to harness the DF methodology, rather than or fight hundreds and hundreds of messages.
Remove circular relationships - diamond relationships are OK.
How To Check relationships DDB > Reports > Check Relationships.
Relationship Tree
Circular Relationship
Since children always find their parents in a DD structure, the record in A must be the parent of B and C and Grand parent of D.
Legacy Circular Relationship.One possibility is to remove the
relationship and re-write the legacy app. That would probably contradict what ever reason(s) is keeping the app still running.
Set_relate MyChild.Child_Key to |FN0,0 in WebApp will programmatically clear the relationship when WebApp runtime executes the command but old flx’s will still rely on the relationship in the data file header.
#1 Difference Between Procedural vs. OO Programming
Learning the distinction between using global buffer and dd (and its local buffer) is key.
Clearing a file buffer (clear MyFile) will not automatically clear a dd.
Whenever getting values a user has entered it use dd msgs.
Global Record Buffer
Perfectly legal to code using the global file buffer inside a DD.
The key is to know when.Any DD method that is called as a result
of a request_saveProgrammer can enforce transactions
with Begin_transaction / End_Transaction outside of DD’s.
Procedure Style Code To Deduct Quantity Sold From Inventory
REREAD… // find correct records in bufferMove (Invt.QtyOnHand - OrderItem.qty) to;
Invt.QtyOnHand)Saverecord Invt.qty_on_hand…. // UNLOCK
OOP Style Code To Deduct Quantity Sold From Inventory
Move (Invt.QtyOnHand - OrderItem.qty) to; Invt.QtyOnHand)
Saverecord Invt.qty_on_hand
Same code! Just in different places.
USE Global Buffer In The “Hooks” Called By Request_save
Validate_saveValidate_deleteCreatingDestroyingUpdateBackout
DDO Structures Must Be Seeded
For a DD to find a child the parent buffers must be seeded.
To Save a child DD the parent DDs must be seeded
Transactional integrity is achieved by seeding all values at once and sending save of a child most file.
Saving Grace: Preparing DD Structure (Ex 1)
Seed buffer to find records,Clear MyParentFile_DDMove iSomeValue to MyParentFile.MyFieldSend request_find of MyParentFile_DD EQ ;
MyParentFile.File_Number 1 // index 1Ready to save children records (Set field
changed value, send request_save)
Saving Grace: Preparing DD Structure (Ex 2)
Clear MyFileMove iSomeValue to MyFile.MyFieldFind EQ MyFile by index.1If (found) send find_by_recnum to ; MyFile_DD MyFile.File_Number MyFile.recnum
Recnum Recnum can be used to seed a DD Concept may seem outdated but it is supported. DB Drivers support Recnum – automatic
increment field. Methodology relies on it DF PKG’s.
Clear my fileMove iSomeValue to MyFile.MyFieldFind eq MyFile by recnumSend Find_By_Recnum to myFile_DD
MyFile.file_number MyFile.recnum
After DD Saves
After a save the DDs will clear.Must seed the entire structure again in
order to save again.
Log in routine (ASP Side)If request("Request_method")="POST" Then
UserName = request("User__Name")UserPass = request("User__Pass")MeetingID = request("Meeting__ID")If UserPass<>"" and UserName<>"" then
LoginOk = oLogin.call("Get_Login_User",UserName,UserPass,MeetingID)else
LoginOk = 0end ifIf LoginOk <> 0 then
response.cookies("TopicID")=oLogin.ddValue("Seats.topicid") response.cookies("User_Name")=oLogin.ddValue("Seats.user_name") Rights = "1"
else ' login failed, clear cookies. response.cookies("TopicID")="-1" response.cookies("User_Name")="Invalid" 'response.cookies("Rights")="-1" Rights = "-1" end ifelse Rights = "0"end if
Login Routine (WBO side) function login_user string sUserId string sPass string sMeetingID returns
integer integer hDD iMeetingID Move (oSeats_dd(Self)) to hDD Send Clear of hDD clear topic Move sMeetingID to topic.topicid
Move (Uppercase(sUserid)) to seats.user_name Move (Uppercase(sPass)) to seats.password Move sMeetingID to seats.topicid Move sMeetingID to topic.topicid Send Find of hDD EQ 1 Function_return (current_record(hdd)<>0) end_function
Selection / Pick list
Two different approaches.Option created by HTML code generated
by a method in a WBO DF.
Selection List For Report Report with a hypertext link approach.In WBO Procedure OnBody String sText Send WriteHtmlRowBegin Get AddRecordLink (HtmlEncode(Unit.Last - ', ' + Unit.first)) to ;
sText Send WriteHtmlCell sText 'Class="Data" Align="left"' Send WriteHtmlCell (HtmlEncode(Unit.bldg)) 'Class="Data“ ;
Align="left"' Send WriteHtmlCell (HtmlEncode(Unit.Number)) 'Class="Data“ ;
Align="left"' Send WriteHtmlRowEnd End_Procedure // OnBody
In ASP ZoomURL = "OwnerReportZoom.asp" ' name of zoom file. If blank none.
ASP Zoom generated by WAS
By default it looks to see if a recnum was passed when the page was called.
If so, it will seed the asp forms with a record, otherwise it will be blank.
<% RecID = Request("RecId") Err=oUnit.RequestFindByRecId(“Unit", RecId)%>
Creating HTML Controls In WBOSend WriteHtml ('<select name="BrandClass"
onChange="document.forms[0].submit()">') Send WriteHtml ('<option value="null"><font
color="#FF0000">Select a Category</font>') For_All MktCls By Index.10 Constrain MktCls.Repair_YN EQ "Y" Constrain MktCls.BrandClass EQ sBrandClass Do If ((MktCls.Recnum = iSelected) And (sFirstTime = "N")) Send
WriteHtml ('<option selected value="') Else Send WriteHtml ('<option value="') Move (Trim(MktCls.ClassName)) To sClassName Send WriteHtml (MktCls.Recnum) Send WriteHtml ('" ') Send WriteHtml (MktCls.Recnum) Send WriteHtml ('> ') Send WriteHtml (HtmlEncode(sClassName)) Send WriteHtml ('</option>') End_For_All EndSend WriteHtml ('</select>')
Debugging
Showln’s - Interact with desktop (Locally)NT Event Log (Locally)Send Writehtml (Remote)Remote Output to Writing to a text file.
(Remote)
Cookies vs. Session variables
Cookie will be stored in the browsers memory and will be written to disk if the expiration date is not reached. Usually expire when session ends.
response.cookies(“BillID")=oSaveBillingAddress.DDValue(“CustBAdd.Custdadd_id")
Session variable is only in memory until session ends or times out.
Session(“BillID") = oSaveBillingAddress.DDValue("Custbadd.Custdadd_ID")
Web Enabling With VDF7
All runtimes are compatible with DF Data Files – VDF 7 could web-enable legacy apps
VDF7 can output in XML for business to business solutions.
VDF7 can output in XSL which could then be used to present data (reporting) by adding style sheets and HTML markup
For security reasons, most web solutions use a mirror of DB which is updated on a regular basis. VDF7 module could handle this, not just reading and writing XML documents.