Category: powerbuilder

Unit-test EAF dst component

In case you are a poor PB developer still working on the dead EAF framework., here is the solution to make your life a little bit funner.

Unit-test IM

Lib List:

c:\pb_projects\testApp\src\server\testApp\pb112\integration-test\im_testApp_test.pbl;
c:\pb_projects\testApp\lib\my_client.pbd;
c:\pb_projects\testApp\lib\ext_client.pbd;
c:\pb_projects\testApp\lib\eaf_client.pbd;
c:\pb_projects\testApp\lib\my_shared.pbd;
c:\pb_projects\testApp\lib\eaf_shared.pbd;
c:\pb_projects\testApp\lib\ext_shared.pbd;
c:\pb_projects\testApp\test\tools\pbunit\pbunit.pbd;
c:\pb_projects\testApp\test\tools\pbunit\pbunitfunc.pbd;
c:\pb_projects\testApp\lib\my_eaf_ext_layer_rc.pbl;
c:\pb_projects\testApp\lib\eaf_proxies.pbd;

PBUnit test code:


//instance
n_connection icn_easerver
n_cst_interfaceservice inv_is

//setup

long ll_rc

constant string INI_FILE = "app.ini"

icn_easerver = CREATE n_connection
icn_easerver.driver = "jaguar"
icn_easerver.userID = ProfileString(INI_FILE, "EAServer", "UserID", "jagadmin")
icn_easerver.password = ProfileString(INI_FILE, "EAServer", "Password", "")
icn_easerver.options = ProfileString(INI_FILE, "EAServer", "Options", "")
icn_easerver.location = ProfileString(INI_FILE, "EAServer", "Server", "")+ ":" + ProfileString(INI_FILE, "EAServer", "Port", "")

ll_rc = icn_easerver.ConnectToServer()

IF ll_rc <> 0 THEN
fail("Connection failed: " + string(ll_rc))
return
END IF

inv_is = create n_cst_interfaceservice

inv_is.of_SetRequestor(This)
ll_rc = inv_is.setConnection(icn_EAServer)

inv_is.of_setUserID("jagadmin")
ll_rc = inv_is.setInterfaceManagerName("MyPackage/MyComponent")

// test dst without arg
long ll_rc

datastore lds_target
lds_target = create datastore

ll_rc = inv_is.registerdataset( "session_list" ,lds_target, "fullalways")

ll_rc = inv_is.retrieve("session_list" )

assert(ll_rc = 1)

assert(lds_target.rowcount() > 0)

//test dst with arg
long ll_rc

datastore lds_target
lds_target = create datastore

ll_rc = inv_is.registerdataset( "reg_activity" ,lds_target, "fullalways")
inv_is.addArgument( "reg_activity", "lastndays", 500  )

ll_rc = inv_is.retrieve("reg_activity" )

assert(ll_rc = 1)

assert(lds_target.rowcount() > 0)

Using PowerGen to bootstrap pbl

We are on the process of applying continuous integration to all our powerbuilder projects. One of the challenge is the classic orcascript sometimes failed in bootstrap pbls. e.g. customer exception. The interesting thing is, I can see the objects in the pbl by using yBrowser 10. but the object contains customer exception is missing from pbl when I try to look it in PowerBuilder IDE.

Anyway, PowerGen definately can save me out of this trouble. It’s slow comparing to orca because it’s checking all the depenanct when bootstrapping, but it’s stable.

Something I think PowerGen can improve in the future:

  1. Allow passing parameters into, so I can still use orca to build executable with version number. PowerGen implement this by using their version edit product to do the post build action.
  2. Work path should be dynamic, right now it’s hard-coded in gen file. I have to standardize it. I can change the *.gen file to replace those absolute path to relative one, but why couldn’t make the relative path be default? relative path is very necessary for branching.

Some other reasons for using orca besides PowerGen:

  1. to deploy EAServer component.
  2. orca import is very fast than synchronizing pbl from exported sr* file in PowerGen. This is a must-have feature to branch-merge in subversion, developers should be able to do this without PowerGen installed.

Use NotePad++ to create orca build script quickly

When creating orcascipt, usually I start from the existing PBT file which contains the huge lib list:

LibList "myapp_appl.pbl\\myapp_appl.pbl;myapp_attachment.pbl\\myapp_attachment.pbl;myapp_br.pbl\\myapp_br.pbl;myapp_casefile.pbl\\myapp_casefile.pbl;myapp_casefile_related.pbl\\myapp_casefile_related.pbl;myapp_dddw.pbl\\myapp_dddw.pbl;myapp_disciplinary_action.pbl\\myapp_disciplinary_action.pbl;myapp_licensee.pbl\\myapp_licensee.pbl;myapp_list.pbl\\myapp_list.pbl;myapp_mass_entry.pbl\\myapp_mass_entry.pbl;myapp_registration.pbl\\myapp_registration.pbl;myapp_report.pbl\\myapp_report.pbl;myapp_resource.pbl\\myapp_resource.pbl;myapp_schedule.pbl\\myapp_schedule.pbl;myapp_security.pbl\\myapp_security.pbl;myapp_ticket.pbl\\myapp_ticket.pbl;im_lls_rc_dws.pbl\\im_lls_rc_dws.pbl;im_lls_rc_rpt_dws.pbl\\im_lls_rc_rpt_dws.pbl;pies_shared.pbl\\pies_shared.pbl;reg_01.pbl\\reg_01.pbl;"

I want the orca format like this:

build executable "myapp.exe" "myapp.ico" "myapp.pbr" "yyyyyyyyyyyyyyyyyyy"

build Library "myapp_appl.pbl\myapp_appl.pbl" "" PBD
build Library "myapp_attachment.pbl\myapp_attachment.pbl" "" PBD
build Library "myapp_br.pbl\myapp_br.pbl" "" PBD
build Library "myapp_casefile.pbl\myapp_casefile.pbl" "" PBD
build Library "myapp_casefile_related.pbl\myapp_casefile_related.pbl" "" PBD
build Library "myapp_dddw.pbl\myapp_dddw.pbl" "" PBD
build Library "myapp_disciplinary_action.pbl\myapp_disciplinary_action.pbl" "" PBD
build Library "myapp_licensee.pbl\myapp_licensee.pbl" "" PBD
build Library "myapp_list.pbl\myapp_list.pbl" "" PBD
build Library "myapp_mass_entry.pbl\myapp_mass_entry.pbl" "" PBD
build Library "myapp_registration.pbl\myapp_registration.pbl" "" PBD
build Library "myapp_report.pbl\myapp_report.pbl" "" PBD
build Library "myapp_resource.pbl\myapp_resource.pbl" "" PBD
build Library "myapp_schedule.pbl\myapp_schedule.pbl" "" PBD
build Library "myapp_security.pbl\myapp_security.pbl" "" PBD
build Library "myapp_ticket.pbl\myapp_ticket.pbl" "" PBD
build Library "im_lls_rc_dws.pbl\im_lls_rc_dws.pbl" "" PBD
build Library "im_lls_rc_rpt_dws.pbl\im_lls_rc_rpt_dws.pbl" "" PBD
build Library "pies_shared.pbl\pies_shared.pbl" "" PBD
build Library "reg_01.pbl\reg_01.pbl" "" PBD

Here is how I did it in minutes of work, using NotePad++ relace with regexp:

  1. Replace step 1, this is easy, replace all \\ with \, and ‘;’ with “\n”
  2. Replace step 2, using regexp, replace all
    ^(.*)
    with
    build Library “\1” “” PBD
  3. Done.

Notepad++ reference: http://notepad-plus.sourceforge.net/uk/regExpList.php

PowerUnit changed to NAnt auto-build

Sourceforge stats shown that PowerUnit has not been updated for 2years, this is wrong! I’m currently still actively playing it. The recent changes including:

  1. Using NAnt auto-build each release. The forth part of version number is subversion revision. The lattest one is 119. The subversion repository is at https://powerunit.svn.sourceforge.net/svnroot/powerunit/
  2. Display PB context version of current PBUnit. Be aware that PB context version must match with the test target. If your target is different, you need to rebuild it, either use NAnt, or in your PB IDE.pbunit_ver
  3. I am using PBScc as my current subversion client, tried PushOK for awhile, it’s OK. Comparing this PBSCC, it’s almost same as old PB dev style.
    PBSCC needs a middle area as the buffer/swap between dev machine to source repository, I think some damon process is monitoring this buffer/swap folder whenver checkin happens, then commit the changes to repo behind the scene. This left the problem of my main src folder shown un-commint-changes icon, doing a diff, the base still no showing the new changes commint through PBSCC, force a update, it said finished a merge, even the src file actually not changed at all.
    pbunit_pbscc_folderpbunit_pbscc_workspace

Embed Autodeploy PB component task to CruiseControl

Problem: Any team member can deploy PB component to EAserver from PB IDE, even if they have local code not checked in, and it’s not traceable also (not in EAServer log!). We don’t know who and when touched those component on server.

We tried to put version info in the comments field of component, but it’s not guaranteed. Developer still might forget. If EAServer administrator didn’t version that point, we couldn’t rollback to certain standpoint.

Solution: Auto-deploy from CC.net build task, which is triggered by check in. Ideally should run a test project before deploy.

Similar idea from some websphere developer. We should archieve the ear file, but the orcascript alone can generate it, so call an easerver export instead.

Orcascript command:

build project PBL_NAME PROJECT_NAME SERVER_HOST PORT_NUMBER

Don’t know how to pass userid/password to this orca command. Had to add easerver profile to CC.net machine.

Ant task:

<property name="JAGUAR" value="C:\Program Files\Sybase\EAServer6\bin" />
 <target name="export_package" depends="">
 <echo message="exporting package ..." />
 <!-- <jag_export entity="Package:My_Package" /> -->
 <delete file="jar\My_Package.jar" />
 <!-- nant change executable to program -->
 <exec executable="${JAGUAR}\jagtool.bat" >
 <arg line="-server easdevSecure -host easdev -port 9100  -user jagadmin -password pass export -dir jar/ Package:My_Package" />
 </exec>
 </target>

PB read version number from exe

This is for pB10.5 or later, Unicode style.

FUNCTION ulong GetFileVersionInfoSizeW  &
( REF string lpFilename, REF ulong lpdwHandle ) &
LIBRARY “version.dll”

FUNCTION integer GetFileVersionInfoW &
( REF string lpFilename, REF ulong lpdwHandle, ulong dwLen, &
REF string lpData )  &
LIBRARY “version.dll”

FUNCTION boolean VerQueryValueW &
( REF string lpBlock, string lpSubBlock, REF long lpBuffer, &
REF uint puLen )  &
LIBRARY “version.dll”

SUBROUTINE CopyMemoryA &
( REF string d, long s, long l )  &
LIBRARY “kernel32.dll”  &
ALIAS FOR RtlMoveMemory

FUNCTION ulong GetModuleFileName (ulong hinstModule, ref string lpszPath, ulong cchPath )  &
LIBRARY “KERNEL32.DLL” &
ALIAS FOR “GetModuleFileNameA;ansi”  // ;ansi  required for PB10 or later

// function of_get_version()
ulong  dwHandle, dwLength
string ls_Buff, ls_key
uint   lui_length
long   ll_pointer
string ls_filename  
integer li_rc
string  ls_result = ‘?’
string ls_EXE_FILE_NAME
String ls_fullpath
ulong lul_handle, lul_length = 512

lul_handle = handle( getapplication() )

If lul_handle = 0 Then //IDE MODE
RETURN ‘[IDE mode]’
End if

// running from EXE
ls_fullpath=space(lul_length)
GetModuleFilename( lul_handle, ls_fullpath, lul_length )

//ls_filename = GetCurrentDirectory ( ) + “\” + ls_EXE_FILE_NAME
ls_filename = ls_fullpath

dwLength = GetFileVersionInfoSizeW( ls_filename, dwHandle )
IF dwLength <= 0 THEN RETURN ls_result END IF ls_Buff = Space( dwLength ) li_rc = GetFileVersionInfoW( ls_filename, dwHandle, dwLength, ls_Buff ) IF li_rc = 0 THEN RETURN ls_result END IF // the strange numbers below represents the country and language // of the version resource. ls_key = "\StringFileInfo40904e4\FileVersion" IF NOT VerQueryValueW( ls_buff, ls_key, ll_pointer, lui_length ) OR & lui_length <= 0 THEN ls_result = "?" ELSE ls_result = Space( lui_length ) CopyMemoryA( ls_result, ll_pointer, lui_length*2 ) // unicode END IF return ls_result [/sourcecode] More to investigate: 1. How to deal with component on EAServer? Couldn't find solution from WCF architecture. Don't we care the version number of a remote service?

workdir setup in PB+PuskOK+SVN+CC.net mode

pb_dir

Ideas:

  1. Move all source file into src folder, keep pbw and pbt file in same level, otherwise PUSHOK plugin will add relative path into pbg files, which is wrong, orca will fail due to this.
  2. Add pbl into ignore list, this file keep changing and we don’t want it in repository.
  3. Create 2 build script, one for incremental build , another for full build with delete pbl, import/migrate and create exe.
  4. dist folder for distribution, should keep zip file for executable and pbls. When new member join in, after GLV from svn repository, should extra the pbl from the zip file in this folder.
  5. orca can build in offline mode, because svn will keep all the sr* file updated.
    Offline mode is only available in PB10 or above.

    start session
    scc set connect property logfile "buildfromsource.log"
    
    ;**********************************************************************
    ; connect mode
    ; read scc info from workspace
    ;**********************************************************************
    ;SCC get connect properties "pbunit.pbw" 
    ;scc connect 
    ;scc set target "pbunitgui.pbt"  OUTOFDATE 
    ;scc refresh target migrate
    
    ;**********************************************************************
    ; offline mode
    ; sr* file should already in the src folder done by svn
    ;**********************************************************************
    scc connect offline
    scc set target "pbunitgui.pbt"  IMPORTONLY
    scc refresh target migrate  
    
    scc close 
    ...build exe...
    

SVN with PB

Tried 3 different svn clients for PB today.

  1. PBSccProxy
  2. Unified SCC
  3. PuskOK SvnSCC

I couldn’t find how to connect to a remote svn repository through PBSccProxy. Thanks to Juan, it seems PBSCC is checking in to local repository mirror folder first, then commit to remote repo. I think the IDE won’t have the out of sync flag then, need to comfirm.

Unified SCC needs me to uncheck the “Delete PB generated file” option, which made my working folder a huge mess. Also, I kept getting NullRefException when doing show diff in IDE. The readonly flag of those sr files always confused me.

PushOK is the most user-friendly one, fast, and clean. I think it doesn’t need Tortoise at all. But merge has to do through tortoise and based on sr? files, then it’s better to turn off “Delete PB generated file option”, otherwise the out-of-sync warning sign on folder will become very annoying.

Something new when using PB with SVN:

  • We can check in distributed files into repository along with source file. To do this, set the global ignore list to “*.pbl, bin/*”, then use ant task to build executable and zip the pbl moving to a “dist” folder, which can be imported into repository as a different folder. Developers just need to extract it to quickly setup the working environment.My ignore list: ” *.pbl, executable/*, *.log, *.pbc”
  • Instead of “sharing” in vss, we do “branch and merge”. Create a branch is easy, the hard part is merge back to trunk (re-integrate branch). One important thing is, there must be a working folder mapping to trunk on merging operator’s machine. Another thing shocked me is, tortoise can only do “tree merge”, which means we can only merge a “folder” back to trunk. I tried to merge one single file using Tortoise, always failed with an error said: source_url@revision must ancestrally related to dest_url@revision#”, switching back to folder/tree level merge, it passed.
  • Merge a single file from trunk to a branch is OK.

I think that’s the way it works. We should consider those cross-project files as a separated library, we don’t want just merge one single object and release. Test whole, release whole.

    <target name="build" depends="delete_last_build">
        <exec executable="orcascr9.exe" failonerror="true" >
            <arg line='/D version_nums="${year},${major},${minor},${build}" ${basedir}/build.orca' />
         </exec>        

        <antcall target="deploy" />        
    </target>

    <target name="delete_last_build">
        <delete>
            <fileset dir="${bin.dir}" includes="*.exe"  />
            <fileset dir="${bin.dir}" includes="*.pbd"  />
        </delete>
    </target>

    <target name="deploy">
        <move todir="${bin.dir}">
            <fileset dir="${basedir}/" includes="*.exe"  />
            <fileset dir="${basedir}/" includes="*.pbd"  />   
        </move>

        <antcall target="archive" />    
    </target>

    <target name="archive">
        <delete file="${dist.dir}/source.zip" />
        <delete file="${dist.dir}/executable.zip" />

        <zip destfile="${dist.dir}/source.zip"
           basedir="${basedir}"
           excludes="**/*.sr*, Executable/"
        />    

        <zip destfile="${dist.dir}/executable.zip"
           basedir="${basedir}"
           includes="Executable/"
        />
    </target>

OrcaScript setting version number into exe

From Sybase’s manual, OrcaScript can set version info into exe,


set exeinfo property <fileversion | fileversionnum | productversion |    productversionnum> versionString
set exeinfo property <companyname | productname | copyright | description>    propertyString

Here is a post talking about how to read this exe info from PB through API . To use this code in PB 9 or later, you should change it to ansi or GetFileVersionInfoSizeW() andGetFileVersionInfoW() API functions.

Because OrcaScript should be able to pass variable through ‘/D’ switch, but my experience is that there are some keywords you should avoid, like ‘version’ (undocumented)

My ant task to pass args.

<project name="build" default="build"
       basedir="../">

    <target name="build" depends="delete_last_build">
        <exec executable="orcascr100.exe" failonerror="true" >
            <arg line='/D version="1.2.3.4" ${basedir}/build/build.orca' />
         </exec>        

        <antcall target="deploy" />
    </target>

    <target name="delete_last_build">
        <delete>
            <fileset dir="${basedir}/Executable" includes="*.exe"  />
            <fileset dir="${basedir}/Executable" includes="*.pbd"  />
        </delete>
    </target>

    <target name="deploy">
        <move todir="${basedir}/Executable">
            <fileset dir="${basedir}/" includes="*.exe"  />
            <fileset dir="${basedir}/" includes="*.pbd"  />
        </move>
    </target>

 </project>

My orca:

start session

set lib_list = “C:\workspaces\pbunit\pb102\pbunit.pbl;”
set lib_list += “C:\workspaces\pbunit\pb102\pbunitfunc.pbl;”
set lib_list += “C:\workspaces\pbunit\pb102\pbunitui.pbl;”

set main_pbl = “C:\workspaces\pbunit\pb102\pbunitui.pbl”

;**********************************************************************
; Set up the Application
;**********************************************************************
set liblist lib_list
set application main_pbl “pbunit”

build application FULL

;**********************************************************************
; Now build the exe and PBD
;**********************************************************************

set exeinfo property companyname “Frank Mao”
; The variable name should avoid keywords, like “version”
set exeinfo property fileversion version_num
build executable “pbunit.exe” “pbunit.ico” “pbunit.pbr” “yyy”

build Library “pbunit.pbl” “” PBD
build Library “pbunitfunc.pbl” “” PBD
build Library “pbunitui.pbl” “” PBD

end session

The challenge for me is, I have to pass both fileversion and fileversionnum to orca, one is string, the other one is 4 integers. If I set version number is this way,

set exeinfo property fileversion  “7.1.0.41”
set exeinfo property fileversionnum  “7,1,0,42”
set exeinfo property productversionnum  “7,1,0,43”
set exeinfo property productversion  “7.1.0.44”

I got

pbunit_version1

Notice the one with coma being converted to dots. fileversionnum really means 4 parts of fileversionnum, must be in the format of “1,2,3,4”.

Or I can just pass the version in coma way, the File version shown on the top is in dot format, while the one in “Other version information” will still in coma way. Who cares? And how many people can tell the difference?

Strange. File version and File version number are totally different concept. They can be different.

Bruce Armstrong’s 5 years old article is still very usesful for orca users.

JSP consume PB component

Spent some time on this, here is the solution. 

 

<%
java.util.Properties props = new java.util.Properties();
props.put( "org.omg.CORBA.ORBClass", "com.sybase.CORBA.ORB" );
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(( String[] ) null, props ); 

FranksPlayGround.n_tt n_tt = FranksPlayGround.n_ttHelper.narrow( orb.string_to_object("FranksPlayGround/n_tt"));
short a = 1;
short b = 4;
short c = 3;
short sum = n_tt.add(a, b, c);
out.println(sum);
%>

This is so called API coding style. Creating PB component is easy, the important part is to ‘generate stub’ at server side. (Those Helper, Holder and Operations classes in folder “EAServer\java\classes” which n_tt inherited from ) 

The reason I back to this is we have a project needs to get a string type of return value from EAServer. Here is what we did: 

  1. Create a dst contains a dummy datawindow. (dst is from EAF)
  2. In createcontent event we call a method from component.
  3. Save this return value into dst’s message, then pass it back to JSP.
  4. JSP has to do some fancy parsing to get this value out.

Seems a lot of work, but we don’t need to do ‘generate stub’ because EAF install wizard already did this for dst. We kind of like using an empty dst passing value down to JSP. 

I can refactor this a little bit by creating a seperate service object (not component) to wrap the component call.