390 likes | 568 Views
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.
E N D
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 web • Enable Data entry • Remote access via the web • WebApp 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’s • We 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.
Circular Relationship A • 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. B C 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_save • Programmer 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 buffer Move (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_save • Validate_delete • Creating • Destroying • Update • Backout
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_DD Move iSomeValue to MyParentFile.MyField Send request_find of MyParentFile_DD EQ ; MyParentFile.File_Number 1 // index 1 • Ready to save children records (Set field changed value, send request_save)
Saving Grace: Preparing DD Structure (Ex 2) Clear MyFile Move iSomeValue to MyFile.MyField Find EQ MyFile by index.1 If (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 file Move iSomeValue to MyFile.MyField Find eq MyFile by recnum Send 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 = 0 end if If 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 if else 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 WBO Send 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 End Send 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.