Progress 4GL Exclusive Lock Not Releasing - session

I'm using proserve to enable multiple-user session.
This is my code in a mouse double-click trigger on my browse:
DO WITH FRAME MAIN-FRAME:
IF EMP-BROWSE:NUM-SELECTED-ROWS > 0 THEN
DO:
EMP-BROWSE:FETCH-SELECTED-ROW(1).
FIND CURRENT EMPLOYEE NO-ERROR NO-WAIT.
IF AVAILABLE (EMPLOYEE) THEN
DO:
DO TRANSACTION ON ERROR UNDO, LEAVE:
C-Win:SENSITIVE = NO.
FIND CURRENT EMPLOYEE EXCLUSIVE-LOCK.
MESSAGE STRING(EMPLOYEE.emp-num) + " locked.".
C-Win:SENSITIVE = YES.
END.
RELEASE EMPLOYEE.
END.
ELSE IF NOT AVAILABLE (EMPLOYEE) THEN
DO:
MESSAGE "The employee details is currently in-use in another session. Please try again later." VIEW-AS ALERT-BOX TITLE "System Message".
RETURN NO-APPLY.
END.
ELSE
DO:
MESSAGE "The record has been deleted in another session.".
RETURN NO-APPLY.
END.
END.
END.
SCENARIO:
Session A double clicks on browse record 1. It will then message something like "2001 locked." after that Session B double clicks on browse record 1 and it will fire the message at the IF NOT AVAILABLE (EMPLOYEE) block.
My question is shouldn't the RELEASE EMPLOYEE code enable session B to access the same record?
I have also tried FIND CURRENT EMPLOYEE NO-LOCK and putting either of the code inside and outside the DO TRANSACTION block but nothing happens.
EDIT:
I applied these changes but in the same scenario, Session B gets the message on the ELSE block which is MESSAGE "The record has been deleted in another session.". What am I doing wrong here?
When I add a call to a new window RUN newWindow.w. after MESSAGE STRING(EMPLOYEE.emp-num) + "locked."., with the same scenario while not closing the new window on Session A I get a proper response of the record being used in another session in Session B.
DO WITH FRAME MAIN-FRAME:
IF EMP-BROWSE:NUM-SELECTED-ROWS > 0 THEN
DO:
EMP-BROWSE:FETCH-SELECTED-ROW(1).
FIND CURRENT EMPLOYEE NO-LOCK NO-ERROR NO-WAIT.
DEFINE VARIABLE iOk AS LOGICAL NO-UNDO.
DEFINE BUFFER myEMPLOYEE FOR EMPLOYEE.
iOk = NO.
DO FOR myEMPLOYEE TRANSACTION:
FIND myEMPLOYEE WHERE myEMPLOYEE.emp-num = EMPLOYEE.emp-num EXCLUSIVE-LOCK NO-ERROR NO-WAIT.
IF AVAILABLE (myEMPLOYEE) THEN
DO:
IF LOCKED (myEMPLOYEE) THEN
DO:
MESSAGE "The employee details is currently in-use in another session. Please try again later." VIEW-AS ALERT-BOX TITLE "System Message".
RETURN NO-APPLY.
END.
ELSE
DO:
C-Win:SENSITIVE = NO.
MESSAGE STRING(EMPLOYEE.emp-num) + " locked.".
C-Win:SENSITIVE = YES.
END.
END.
ELSE
DO:
MESSAGE "The record has been deleted in another session.".
RETURN NO-APPLY.
END.
END.
END.
END.

RELEASE does not do what you think it does. Use of RELEASE is a red flag. It almost always means that the coder does not have a firm grasp of record and transaction scoping issues.
From the docs:
RELEASE
Verifies that a record complies with mandatory field and unique index definitions. It clears the record from the buffer and unites it to the database if it has been changed.
Notice how the definition says nothing about locks?
Embedding user interface within a transaction is a major mistake -- doing that essentially guarantees that you will have lock contention issues and scalability problems going forward.
As Jens points out your whole approach needs rethinking.
In order to properly control the record and transaction scope the best practice is to be very explicit about it and confine the update to a very tight block of code. Ideally you encapsulate it in a procedure or function like so:
function updEmpName returns character ( input empNum as character, input newName as character ):
define buffer employee for employee.
do for employee transaction:
find employee exclsuive lock where employee.employeeNum = empNum no-error.
if locked employee then
return "locked".
else if available employee then
do:
assign
employee.name = newName.
.
return "updated".
end.
else
return "no such employee".
end.
end.
If you cannot put the update in a function or procedure then you need to "strong scope" the buffer being used to update the record:
define variable ok as logical no-undo.
define buffer updEmployee for employee.
ok = no.
do for updEmployee transaction:
find updEmployee exclusive-lock where updEmployee.employeeNum = empNum no-error.
if available updEmployee then
assign
/* update whatever fields are needed */
ok = yes
.
end.
The "DO FOR updEmployee" is called "strong scope". That means that there cannot be "free references" to the updEmployee buffer outside that block. If the compiler complains that there is a problem then you need to fix it becuase your scope f not what you think it is.

I think you have some things to reconsider here.
This row:
FIND CURRENT EMPLOYEE NO-ERROR NO-WAIT.
Will result in a SHARE-LOCK. Some people (including me) tend to avoid those. I would change into:
FIND CURRENT EMPLOYEE EXCLUSIVE-LOCK NO-ERROR NO-WAIT.
The row after:
IF AVAILABLE (EMPLOYEE) THEN
Will only deal with the existence of the record - not it's locking status. All in all, consider the online (F1) help:
LOCKED function
Returns a TRUE value if a record is not available to a prior FIND . . . NO-WAIT statement because another user has locked a record.
AVAILABLE function
Returns a TRUE value if the record buffer you name contains a record and returns a FALSE value if the record buffer is empty.
FIND...NO-ERROR
Suppresses ABL errors or error messages that would otherwise occur and diverts them to the ERROR-STATUS system handle. If an error occurs, the action of the statement is not done and execution continues with the next statement. If the statement fails, any persistent side-effects of the statement are backed out. If the statement includes an expression that contains other executable elements, like methods, the work performed by these elements may or may not be done, depending on the order the AVM resolves the expression elements and the occurrence of the error.
FIND...NO-WAIT
Causes FIND to return immediately and raise an error condition if the record is locked by another user (unless you use the NO-ERROR option on the same FIND statement). For example:
You could change this into (and the ELSE statement to something like):
FIND CURRENT EMPLOYEE EXCLUSIVE-LOCK NO-ERROR NO-WAIT.
IF LOCKED(EMPLOYEE) THEN DO:
MESSAGE "Locked by another user".
END.
ELSE DO:
IF AVAILABLE (EMPLOYEE) THEN DO:
MESSAGE "Go ahead and change".
END.
ELSE DO:
MESSAGE "Not available".
END.
END.
Have in mind that you are writing code that's a mix between GUI, data access and business logic. This is a bad practice. You can do it for smaller examples, tests and things like that but you should actually avoid it. Even if you're not using AppServer right now you will most likely want to do that in the future - if the code already have nice separation of concern (for instance GUI, business logic and data access) future modernization will be so much easier.

The command FETCH-SELECTED-ROW() already put the selected record in browse into the record buffer using the LOCK that's defined within BROWSE definition, then you don't need to use a FIND statement to do so. Anyway, when I need to do things like you trying to do I use a code like this one :
DO WITH FRAME {&FRAME-NAME}:
DO iCount = 1 TO brEmployee:NUM-SELECTED-ROWS:
brEmployee:FETCH-SELECTED-ROW(iCount).
IF AVAIL Employee THEN DO:
FIND CURRENT Employee EXCLUSIVE-LOCK.
/* Do whatever I need */
FIND CURRENT Employee NO-LOCK.
END.
ELSE DO:
IF LOCKED(Employee) THEN DO:
MESSAGE 'Record locked!'
VIEW-AS ALERT-BOX INFO BUTTONS OK.
END.
ELSE DO:
MESSAGE 'Record deleted!'
VIEW-AS ALERT-BOX INFO BUTTONS OK.
END.
END.
END.
END.
In this example, the FETCH-SELECTED-ROW() aways return the record. If it isn't available you can check LOCKED condition. To avoid the browse to return records that doesn't exist in database you can use -rereadnolock session parameter.
But you're using the code within MOUSE-SELECT-DBLCLICK event of your browse. Then, you can replace the code with something like this :
DO WITH FRAME {&FRAME-NAME}:
brEmployee:SELECT-FOCUSED-ROW() NO-ERROR.
IF AVAIL Employee THEN DO:
FIND CURRENT Employee EXCLUSIVE-LOCK.
/* Do whatever I need */
FIND CURRENT Employee NO-LOCK.
END.
ELSE DO:
IF LOCKED(Employee) THEN DO:
MESSAGE 'Record locked!'
VIEW-AS ALERT-BOX INFO BUTTONS OK.
END.
ELSE DO:
MESSAGE 'Record deleted!'
VIEW-AS ALERT-BOX INFO BUTTONS OK.
END.
END.
END.
Hope it helps.

Related

Discord.py Getting a number of messages deleted from audit log

I'm making a logging system with discord.py. For now, I'm trying to get the number of messages were deleted from a single audit log entry (since sometimes the audit logs updates an entry instead of creating a new one), so I tried with "entry.action.count". Here's my code
#client.command()
#commands.has_permissions(administrator=True)
async def deletecount(ctx):
async for entry in ctx.guild.audit_logs(limit=1, action=discord.AuditLogAction.message_delete):
print(entry.action.count)
But instead of printing the number of messages deleted, it just prints
<built-in method count of _EnumValue_AuditLogAction object at 0x000001FF9769C640>
From this screenshot, in this case the latest entry has 5 deleted messages. I'm trying to print out that "5". So how do I get the number of message deleted from the latest audit log entry? The documentation doesn't have further information on how to use the extra attribute for entry.action.
I should also add that I have asked someone about this, they suggested to try print(entry.action.count()), but I got
TypeError: tuple.count() takes exactly one argument (0 given)
What is the argument that should be given there? Thanks in advance.
It is possible, use the extra attribute and it will return a dict with the data you want (the docs don't give you a lot of information about it because the return value differs from the type of action):
async for entry in ctx.guild.audit_logs(limit=1, action=discord.AuditLogAction.message_bulk_delete):
count = entry.extra['count']
The return value of AuditLogEntry.extra varies depending on the type of event, the example above will only work with message_bulk_delete
PS: You were using the wrong action in the iterator, it should be AuditLogAction.message_bulk_delete not AuditLogAction.message_delete
References:
AuditLogEntry.extra

Change error system message in oracle apex

I have a form page and all field is required when press save the below message appear
How i can change this message to custom message "please fill all required fields " , and how i can clear error when enter value (when value change to not null).
I can't see images at the moment.
However, one option might be to create your own validation which returns error text. Something like
if :P1_NAME is null then
return ('Name must be entered');
end if;
Messages are automatically cleared once you submit the page and there are no errors left.
I am not sure if you can change system messages but you can add custom error messages with javascript if a change happens in any item.
Add a change event to the item that runs javascript and use the following code:
var item = apex.item('P1_ITEM').getValue();
if(item == null) {
//First clear the errors
apex.message.clearErrors();
// Now show new errors
apex.message.showErrors([
{
type: "error",
location: [ "page", "inline" ],
pageItem: "P1_ITEM",
message: "Name is required!",
unsafe: false
},
{
type: "error",
location: "page",
message: "Page error has occurred!",
unsafe: false
}
]);
}
However, this will not stop the user from submitting, it only allows you to better display the messages, so you must add the corresponding validations after submit.
If you want to remove the system error message from the required items, you can disable the option of Value Required on item and add a custom validation as they told you in the other response.
If you want to explore all the apex.message options better, I recommend this documentation:
https://docs.oracle.com/database/apex-5.1/AEAPI/apex-message-namespace.htm#AEAPI-GUID-D15040D1-6B1A-4267-8DF7-B645ED1FDA46
More documentation for apex.item:
https://docs.oracle.com/cd/E71588_01/AEAPI/apex-item.htm#AEAPI29448
There are some ways for how to do such things.
Firstly you have the custom Validations you can make, these are awesome and you should really try to use them if possible.
Then there is also the Error message on the saving procedure, but this just throws a custom message on procedure fail so I never use it.
What you appear to be seeing there is that you got an error message and didnt change the fields associated with the error.
If the save procedure is custom, you can also put in an EXCEPTION block before the END, and catch errors there and throw out a custom error with a custom error message.
Another thing I really like is to actually rename some common errors so I dont have to catch them all individually. Say clients may often times try to save identical data, thus breaking the PK. Oracle will throw an error, but the message is good for the developer, but less understandable for the client whom I always assume is a 3 year old kid who can barely read and will cry over everything. So I make an error handling function, add it to apex, and so when the error occurs, it throws a nice message informing the client that they have tried to add some data that already exists.
So, an error handling function associated with APEX, to rename some normal errors.
Good luck

Form failed when called

in oracle form builder my form does not access trigger named WNFI i call it on when- new- form-instance like this
execute_trigger('WNFI') ;
it doesn't enter it at all
what is the problem ?
Which Forms version do you use?
I've just tried it on Forms 10g, and it works OK - trigger is called and does its job (in my test, displays a message).
Anyway, just in case you didn't understand what APC says: move code from the WNFI trigger into a procedure (not a stored procedure, in the database; create it under the Program units node in the Object Navigator). For example:
-- This is P_WNFI procedure; this code was previously in WNFI trigger
message('wnfi');
message('wnfi');
Modify WHEN-NEW-FORM-INSTANCE trigger:
-- instead of
-- execute_trigger('WNFI');
-- use
p_wnfi;
If you open Forms Online Help system and search for execute_trigger, you'll see a note:
EXECUTE_TRIGGER is not the preferred method for executing a user-named trigger. Writing a user-named subprogram is the preferred method.
If I were you, I'd do as told. execute_trigger might be here for backward compatibility for some time, but not forever. Who knows; maybe Oracle corp. does.

SAP ABAP Table control

I'm new to SAP/ABAP programming. I'm really having trouble displaying a simple table control out of my internal table. Could you please help? Currently, if I search for the course by entering a course ID - 10001, it displays a blank table control. It should display the content which i've already populated. I followed exactly from this source: http://sapabap-4.blogspot.sg/2013/06/simple-table-control.html.
The only difference is that I've placed everything in a report program instead of module program as my lecturer asked me to. Also do note that there's absolutely no errors, the table control just doesn't show.
*-------Declaration of tables for screen fields------------------------*
TABLES: zcms_courses.
*------Declaration of required structures------------------------------*
TYPES: BEGIN OF ty_zcms_courses,
course_id TYPE zcms_courses-course_id,
course_content TYPE zcms_courses-course_content,
music_genre TYPE zcms_courses-music_genre,
options TYPE zcms_courses-options,
course_name TYPE zcms_courses-course_name,
END OF ty_zcms_courses.
*-----Declaration of user command variables----------------------------*
DATA: OK_CODE TYPE sy-ucomm,
OK_CODE1 TYPE sy-ucomm.
*-----Declaration of work area & table---------------------------------*
DATA: wa_zcms_courses TYPE ty_zcms_courses,
itab_zcms_courses_hd TYPE TABLE OF ty_zcms_courses.
*---------Declaration of Table Control---------------------------------*
CONTROLS: zcms_courses_tc TYPE TABLEVIEW USING SCREEN 9002.
MODULE status_9001 OUTPUT.
SET PF-STATUS 'PF_PO_INP'.
SET TITLEBAR 'PO_TITLE'.
ENDMODULE. " status_9001 OUTPUT
MODULE user_command_9001 INPUT.
"Call screen 9001.
CASE OK_CODE.
WHEN 'DISP'. "Display button
CALL SCREEN 9002.
"PERFORM get_po.
WHEN 'CLR'. "Clear button
CLEAR zcms_courses-course_id.
WHEN 'BACK' OR 'EXIT' OR 'CANCEL'.
LEAVE PROGRAM.
ENDCASE.
ENDMODULE.
MODULE status_9003 OUTPUT.
SET PF-STATUS 'PF_PO_INN'.
SET TITLEBAR 'PO_TITLE1'.
ENDMODULE.
module user_command_9003 input.
CASE OK_CODE1.
WHEN 'ADD'.
CALL SCREEN 9003. endcase.
endmodule.
FORM get_po .
IF zcms_courses-course_id IS NOT INITIAL.
REFRESH: itab_zcms_courses_hd .
SELECT SINGLE course_id course_content music_genre options course_name
FROM zcms_courses INTO wa_zcms_courses
WHERE course_id = zcms_courses-course_id.
IF sy-subrc = 0.
SELECT course_id course_content music_genre options course_name
FROM zcms_courses INTO TABLE itab_zcms_courses_hd
WHERE course_id = wa_zcms_courses-course_id.
IF sy-subrc = 0.
SORT itab_zcms_courses_hd.
"Refreshing the table control to have updated data
REFRESH CONTROL 'ZCMS_COURSES_TC' FROM SCREEN 9002.
CALL SCREEN 9002.
ENDIF.
ENDIF.
ENDIF.
ENDFORM.
MODULE STATUS_9002 OUTPUT.
SET PF-STATUS 'PF_PO_INP'.
SET TITLEBAR 'PO_TITLE'.
ENDMODULE.
MODULE table_control OUTPUT.
DESCRIBE TABLE itab_zcms_courses_hd LINES sy-dbcnt.
zcms_courses_tc-current_line = sy-loopc.
zcms_courses_tc-lines = sy-dbcnt.
zcms_courses-course_id = wa_zcms_courses-course_id.
zcms_courses-course_content = wa_zcms_courses-course_content.
zcms_courses-music_genre = wa_zcms_courses-music_genre.
zcms_courses-options = wa_zcms_courses-options.
zcms_courses-course_name = wa_zcms_courses-course_name.
CLEAR wa_zcms_courses.
ENDMODULE.
MODULE user_command_9002 INPUT.
CASE ok_code.
WHEN 'BACK' OR 'EXIT' OR 'CANCEL'.
CLEAR ok_code.
LEAVE LIST-PROCESSING.
LEAVE TO SCREEN 9001.
ENDCASE.
ENDMODULE.
MODULE modify_table_control INPUT.
READ TABLE itab_zcms_courses_hd INTO wa_zcms_courses INDEX
zcms_courses_tc-current_line.
IF sy-subrc = 0.
MODIFY itab_zcms_courses_hd FROM wa_zcms_courses INDEX
zcms_courses_tc-current_line.
ENDIF.
ENDMODULE.
__________________________________________________________________________________
FLOW LOGIC of screen 9002:
PROCESS BEFORE OUTPUT.
MODULE status_9002.
LOOP AT itab_zcms_courses_hd INTO wa_zcms_courses WITH CONTROL
zcms_courses_tc.
MODULE table_control.
ENDLOOP.
PROCESS AFTER INPUT.
LOOP AT itab_zcms_courses_hd.
MODULE modify_table_control.
ENDLOOP.
MODULE user_command_9002.
__________________________________________________________________________________
FLOW LOGIC of screen 9001
PROCESS BEFORE OUTPUT.
MODULE STATUS_9001.
PROCESS AFTER INPUT.
MODULE USER_COMMAND_9001.
If your program is a report, you can use ALV functions.
This is a simple tutorial.
https://wiki.scn.sap.com/wiki/display/ABAP/ALV+easy+tutorial
Or, if you need more help, I can pass some code from my programs.
The reason you're not seeing errors is basically because those are all valid ABAP commands.
However, the PAI/PBO logic you're using (e.g. MODULE user_command_9001 INPUT) should be tied to SCREEN objects and not directly inside a REPORT object.
Think of it in this way: REPORTs can display data (e.g. an ALV) without you having to explicitly create a SCREEN object. The problem here is that a table control needs to be tied to a SCREEN to be properly displayed, so it's best to use an ALV to display data inside a REPORT.
I know this might be confusing at first, but usually you would use a MODULE POOLwhen a lot of interaction between the user and the screen is needed and that's exactly the case with a table control.
You can find a modern ALV example here: ALV Grid for In-Line Declaration
And additional examples in package SALV_OM_OBJECTS.

Need help for HP UFT -- Vb Script code

Firstly, I will give you some idea about what I am doing. I writing a few test cases without a framework, so for this I am creating a VBS file in which have written a code for launching of the UFT and opening of the action have created. Code is like this:
Set qtApp= CreateObject("QuickTest.Application")
qtApp.Launch
qtApp.Visible = True
Set qtTest = qtApp.Test
'And then call your scripts one after another
qtApp.Open "D:\UFT\TestScript\Function_Demo",true
Set qtTest = qtApp.Test
qtTest.Run
Also have created functions for each scenario which are associated with this Action, so whenever VBS file will be triggered by third system it will launch UFT and open the Action in which I am calling all the functions, so here comes my first question
How can I set the priority of the function so that it will run in sequence?
Also I wanted to write code for the result status as pass and fail and depending on the pass and fail status I wants to update the XML specific nodes with the status and also along with this wants to capture the screenshot of the test case whenever it's failed and for this also have to update the path in XML nodes.
As I am very new to the UFT so was not sure how to do this can someone please help me with this? How to write the code and what all things will be required?
Quick response will be appreciated.
Regarding Function Priority :-
Function in Test Action always takes priority over any other function with same name in any other function libs.
Second Priority is Function from Lib which is executed last[if u using loadfunctionlib or executefunctionlib]
Regarding XML specific nodes :-
Look for Res/Report/results.xml Summary Node
ex :- Summary sTime="*************" eTime="**************" passed="128" failed="2" warnings="36" /Summary
Question 2: UFT provides a function to update the results:
Reporter.ReportEvent micPass, "Step 1", "This step has passed. The object was found"
Reporter.ReportEvent micFail, "Step 1", "This step has failed. The object was not found"
it also provides a function to store the screenshot of the enire desktop
Desktop.CaptureBitmap "C:\Temp\HP-UFT-Test.png",true
Reporter.ReportEvent micFail, "Step", msg, "C:\Temp\HP-UFT-Test.png"
Its also possible to capture a screenshot of specific, objects for instance:
WpfWindow("SAP Work Manager").CaptureBitmap "C:\Temp\HP-UFT-Test.png",true
Reporter.ReportEvent micFail, "Step", msg, "C:\Temp\HP-UFT-Test.png"

Resources