cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - You can Bookmark boards, posts or articles that you'd like to access again easily! X

Translate the entire conversation x

How to register callback when Arbortext is truly exiting

ehood
10-Marble

How to register callback when Arbortext is truly exiting

Product: PTC Arbortext Editor 8.2.2.1

 

The documentation for the session_add_callback and how the 'quit' callback function is invoked is confusing with regard to how the quit code is documented.  I need the ability to do some proper cleanup with Arbortext process is closing, but not sure how to reliable do this with a quit callback.

 

The code that is passed to the quit callback is 0, 1, 2, but some testing has shown that when called with a '0', the application may not fully exit if the user decides to cancel the quit (e.g. has unsaved edits).  For example, the following scenario happens:

  1. I select Exit from the File menu while there is unsaved changes.
  2. My callback is called with code equal to 0.  Since I do not know if the application is truly exiting, I have the callback do nothing.
  3. The Arbortext then pops up the do-you-want-to-save changes dialog.
  4. I cancel the dialog, so the application is still running.
  5. I choose exit again.
  6. Unsaved-changes dialog pops up again (my callback is NOT called). 
  7. Choose to not save changes to exit application (again, my callback is NOT called).

After canceling the first dialog, my callback is not called again when the application truly exits., so I am unable to do proper cleanup.  Is there any reliable way via ACL (or Java) to have a callback invoked when Arbortext is truly exiting, when the user no longer has options to cancel it.

ACCEPTED SOLUTION

Accepted Solutions

I just did a quick test, and my quit callback continues to trigger on subsequent exits even if the user is prompted to save and selects "cancel". I'm not sure why yours stops after the first cancel. You don't have anything in your code that removes the session callback explicitly, do you? My quit callback is very simple:

function bye(code, system) {
  response("Bye! code = " . code . ", system = " . system);
  return 0;
}

session_add_callback("quit", bye);

You can check whether the user will get prompted by looking for any modified documents, something like this:

function check_dirty_docs() {
  local docs[];
  doc_list(docs, 0x64); # only check main documents, modify flags as needed
                        # if you use entities or xincludes
  local dirty = 0;
  for (d in docs) {
    if (modified(docs[d])) {
      dirty = 1;
    }
  }
  return dirty;
}

If there are no dirty documents, then there won't be any save prompt, and you can run your cleanup code. If there are dirty documents, you could possibly write your own prompt to ask them to save or discard changes. It's up to you if you want to offer them the chance to cancel there. If you don't, you're good--just run exit or quit ok to save all or discard all (respectively). If do you offer the cancel option, and they choose it, you can return -1 from the callback, but if it's working right, it should trigger again after the user finishes and re-quits.

View solution in original post

9 REPLIES 9

I guess it depends what kind of "proper cleanup" you are trying to do. Would it make sense to do the cleanup when the document is destroyed (unloaded from memory) instead? If so, you could possibly use doc_add_callback() to assign a "destroy" callback.

 

Thanks for the response, but unfortunately what you suggest will not work for me.  My extension operates across the entire session of Arbortext Editor, which can include multiple documents being opened.  I need to know when the Editor process itself is terminating, and where the user does not have the option to cancel termination.

 

To provide some more context, my extension monitors a folder under the user's local data area (of the operating system), where it checks for command files to be placed by another process.  These command files contains the pathname and location (represented as an XPath expression) within a document that should be opened in Editor.  The file system approach for communication was taken since creating a server process within Editor (I know I can create a listener socket via ACL) is likely not allowed in the environments our customers normally operate under.

 

Where cleanup comes in to play is the termination of the watcher thread running in Editor  (via Java) that monitors the directory.  I would like to perform an action when Editor terminates.  I tried registering a shutdown hook with the JVM, but apparently, the way Editor terminates the JVM prevents my shutdown hook from executing.  Therefore, trying to figure out via ACL on how to hook into the Editor termination process. I have something else I am going to try on the Java-side, but it would be nice if Editor provided the hook I needed.

I just did a quick test, and my quit callback continues to trigger on subsequent exits even if the user is prompted to save and selects "cancel". I'm not sure why yours stops after the first cancel. You don't have anything in your code that removes the session callback explicitly, do you? My quit callback is very simple:

function bye(code, system) {
  response("Bye! code = " . code . ", system = " . system);
  return 0;
}

session_add_callback("quit", bye);

You can check whether the user will get prompted by looking for any modified documents, something like this:

function check_dirty_docs() {
  local docs[];
  doc_list(docs, 0x64); # only check main documents, modify flags as needed
                        # if you use entities or xincludes
  local dirty = 0;
  for (d in docs) {
    if (modified(docs[d])) {
      dirty = 1;
    }
  }
  return dirty;
}

If there are no dirty documents, then there won't be any save prompt, and you can run your cleanup code. If there are dirty documents, you could possibly write your own prompt to ask them to save or discard changes. It's up to you if you want to offer them the chance to cancel there. If you don't, you're good--just run exit or quit ok to save all or discard all (respectively). If do you offer the cancel option, and they choose it, you can return -1 from the callback, but if it's working right, it should trigger again after the user finishes and re-quits.

@ClayHelberg Thanks for the follow-up.  Very informative and I think I understand how Editor is doing things now.  How it behaves put extra burden on the extension developer to register a true shutdown hook as the developer has to replicate the "Do you want to save" prompting that Editor already knows how to do.  I.e. In order for me to do cleanup so the user cannot interrupt it, I have to code the dialogs myself so they can make that save/not-save choices, which includes checking each document if dirty and then prompting.

 

I will mark your response as the Solution, but I am exploring alternatives.  The extension I am developing is something that will be installed in customer's environments I have no control over, where they can have their own set of Editor customizations .  Therefore, I want to be able to hook into Editor in the least obtrusive way possible. so if I go the custom dialog prompting route, unsure how that will play in unknown environments.  I was hoping for a basic "shutdown" hook when Editor is truly terminating.

 

I wish I can rely on JVM shutdown hooks, but the way Editor manages the JVM process, when Editor shuts down, it is preventing any registered hooks from being invoked.  Even calls like File.deleteOnExit() fail, where the file does not get removed on JVM exit.

I agree, it would be nice to have a simple "the very last thing before the program exits" callback hook you could tap into, that would come after the save/discard prompt instead of before. I'm a little surprised that the Java shutdown hooks don't work as expected.

 

Here's something that seems to work a little better: watch for windows closing instead of the application. Apparently the window destroy event happens after all the save/discard/cancel stuff happens. But you have to do a little accounting to make sure you are only responding to the last edit window closing. Try something along this line:

function wbye(win) {
  # ignore non-edit windows closing
  if (window_class(win)!="edit") {
      return 0;
  }
  local wins[];
  window_list(wins, "edit", -1, 0x2); # get top-level edit windows
  if (count(wins)==1) {
      # last one is closing, do your clean up here
      remove_file 'C:\work\test\quit_callback\deleteme.txt'
  }
  return 0;
}

window_add_callback(0, "destroy", wbye); # 0 -> attach to all windows

@ClayHelberg Brilliant! The window_add_callback() approach is definitely better.  Cannot see how to change the accepted solution, but for what I need, the window_add_calback() method appears to solve my problem based on initial testing.  Thanks!

@ClayHelberg Testing your window_add_callback() some more, I discovered a behavioral oddity with Editor where the window count never goes down to 1.  The scenario: I have modified my application to start Editor if it is not already running, then send the command I want to do in editor (basically open a document to  specific location).  When I close the window via the Windows title bar 'X' button, the window closes, but the Editor process does not terminate.  Based on a diagnostic I put in the wbye() function, the window count is "2" and not "1" when the function is called.

 

I speculate there is a race condition in Editor, where the reception from my application to open a document suppresses the empty editor window that normally arises when starting Editor from the Windows Start menu.  I.e. The reason I get a "2" count is there is non-visible window present.

 

Some debugging reveals that the hidden window has an invalid state (-1).

 

I modify the wbye() function to try to address this problem:

function wbye(win) {
  # ignore non-edit windows closing
  if (window_class(win) != "edit") {
    return 0;
  }
  local wins[];
  window_list(wins, "edit", -1, 0x2); # get top-level edit windows
  local cnt = count(wins);
  if (cnt == 1) {
    # If at last window, time to clean up
    # cleanup here...
  } else {
    local w;
    local invalid = 0;
    for (w in wins) {
      local wstate = window_state(w);
      if ((wstate == -1) || (wstate == 0)) {
        invalid++;
      }
    }
    if (invalid == cnt) {
      # clean up here...
      exit_editor(0, 2);
    }
  }
  return 0;
}

 

HOWEVER, it appears Editor, at least the version ( 8.2.2.1) I am using is buggy about window states.  If I open 2 windows, then close 1, then open another, then close 1, the exit_editor() function triggers as all window states are now -1 or 0, even though there is 1 window still visible on the screen.  Note, all window's are opened via ACL window_open() calls and the close is initiated by hitting the "X" in the Windows OS title bar of the window.

 

This oddity is avoided if File->Exit is used to close Editor.

 

I have observed other oddities with Editor that I will likely make separate posts for.

Oh no. I'm sorry that it didn't pan out as smoothly as it looked at first. It's unfortunate that there are so many idiosyncrasies in how Editor handles shutdown.

One other alternative comes to mind: write a batch file (or some other external executable) to start Arbortext, but have it include the cleanup after the Arbortext process exits. In order for this to work, you'd have to make sure your authors always use the batch file instead of the OOTB Arbortext Editor shortcut. But it would make sure you can do your cleanup after the Arbortext session is really, truly, closed down.

 

In fact, now that I look back at your messages, it seems like you are already using an external custom app to launch Arbortext. Could you do the process monitoring there? Have your app watch the Editor process, and do the cleanup there when the process terminates?

@ClayHelberg I dealt with the cleanup situation in my application by checking each time it will perform an Editor action, it checks first to see if the process is running.  If not, it assumes a stale state and resets things before (re)starting Editor.  I cannot assume my application will always start Editor, and it is likely it will not in most cases.  Because of the invalid window problem, it is possible Editor never really shutdowns when last visible window is closed, but it "reawakens" when my application sends another command or the Windows Start shortcut is used. Editor appears to self correct after that.

 

I am now battling Editor race conditions during its initialization, where the my post regarding tag visibility is just one of the symptoms of this that I am struggling with.

Announcements


Top Tags