• Agent Performance

    By Ken Hawkins 2 decades ago

    Many of our users process documents while connected to our Notes server using wireless cellular cards. Connection speed can vary depending upon location and other factors. The complaint is that submitting documents can take over 5 minutes. I've done some preliminary analysis and have determined that the RWF_DOC_QUERYCLOSE agent is responsible for the bulk of the processing time.



    I'm wondering if it's possible to run the RWF agent on schedule (or modified docs?) and have it check for a flag that gets set on submit. This is a Notes client application.



    Can anyone think of any gotchas using this approach?

    • Here's the answer

      By Phillip A Kahrl 2 decades ago

      Ken,



      Running an agent on Notes client uses considerable network bandwidth, so the solution is to run the an agent on the server that does the same thing. I have used this same approach for web applications on a web server that doesn't support Java web triggered agents.


      1. Create a view "(UnprocessedDocs)" that has the selection criteria rwfDocStatus="new status". Below is code for an agent that will process documents in this view. (You will need to import the "rwf All Classes" library by using the "Edit Project" button.) This agent can be run as a scheduled agent, or you can call it from another agent using the "runOnServer" method from Lotus Script or Java.



        Here's the code:



        import lotus.domino.;

        import java.util.
        ;



        public class JavaAgent extends AgentBase {



        public void NotesMain() {



        try {
            Session session = getSession();<br/>
            AgentContext agentContext = session.getAgentContext();<br/>
            Database db = agentContext.getCurrentDatabase();<br/>
            View view = db.getView(&quot;(UnprocessedDocs)&quot;);<br/>
            Vector docs = new Vector(0);<br/>
        <br/>
            Document doc = view.getFirstDocument();<br/>
            while(doc != null){<br/>
                docs.addElement(doc);<br/>
                doc = view.getNextDocument(doc);<br/>
                }<br/>
                        <br/>
            for(Enumeration e=docs.elements(); e.hasMoreElements(); ){<br/>
                doc = (Document)e.nextElement();<br/>
                WorkflowDocument.processContextDoc(doc);<br/>
            }<br/>
        

        } catch(Exception e) {<br/>
            e.printStackTrace();<br/>
        }<br/>
        
        }

        }
      • Thanks . . .

        By Ken Hawkins 2 decades ago

        I appreciate your quick response. I was going to try a similar approach using the rwfHasChanged field. I'll let you know how the implementation performs.





        You going to LotusSphere 2006?

        • View selection

          By Phillip A Kahrl 2 decades ago

          You are correct ,the view selection should be "rwfHasChanged="new status"" not "rwfDocStatus="new status"

          • By Bruno Abreu 2 decades ago

            I got some errors when running the scheduled agent.

            Somebody also got errors?



            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: NotesException: Notes error: No names found to send mail

            to.

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at lotus.domino.local.Document.Nsend(Native Meth

            od)

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at lotus.domino.local.Document.send(Unknown Sour

            ce)

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at lotus.domino.local.Document.send(Unknown Sour

            ce)

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at Mail.sendMail(Mail.java:166)

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at Mail.sendMail(Mail.java:195)

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at Notification.sendMail(Notification.java:351)

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at State.sendMail(State.java:141)

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at WorkflowDocument.sendMail(WorkflowDocument.ja

            va:107)

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at WorkflowDocument.processContextDoc(WorkflowDo

            cument.java:248)

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at JavaAgent.NotesMain(JavaAgent.java:23)

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at lotus.domino.AgentBase.runNotes(Unknown Sourc

            e)

            09-01-2006 12:29:04 AMgr: Agent ('RWF DOC PROCESS' in 'idoc10\producao\ISPA\id

            oc.nsf') error message: at lotus.domino.NotesThread.run(NotesThread.java

            :218)

            • Indicates no mail recipients

              By Phillip A Kahrl 2 decades ago

              This error message indicates that a mail notification that was generated that has no recipients.



              This can occur if you are using a field value for mail recipients and the context document either does not have the field or the field has no values.



              This is not an error with RWF but may indicate that your application setup is not working as expected. In my application I sometime have cases where there are no mail recipients, so this message doesn't necessarily mean something is wrong with your application.



              I do plan to change the way this exception is caught in future versions to avoid confusion.



              I would suggest, settting the email sending to "Disable" in the "email" document of the "rwfConfiguration" view, and then processing documents and checking the resulting saved memos to determine if you memos are getting the recipients as intended.

              • By Bruno Abreu 2 decades ago

                This error do not occur in a "Action Menu Selection Agent".

                In a schedule agent accurs.

                The code is the same.

              • By Bruno Abreu 2 decades ago

                This error do not occur in a "Action Menu Selection Agent".

                In a schedule agent accurs.

                Is the same code.

                • Tried RunOnServer

                  By Ken Hawkins 2 decades ago

                  I tried triggering from another agent via the LS RunOnServer method. Same results . . . the documents are processed, but notifications are not sent.



                  My server didn't log any errors.



                  Next ???

                  • What happens when you run the agent called by "RunOnServer" directly from Notes client?

                    By Phillip A Kahrl 2 decades ago
                    • By Bruno Abreu 2 decades ago

                      This error occurs when you change the |Run on| to "-Any Server-".

                    • Production server

                      By Ken Hawkins 2 decades ago

                      I tried running the "UnprocessedDocs" scheduled agent on our production server. It worked!



                      What bugs me is I don't know why. Both our test and production servers have identical security settings regarding the use of agents. My agent is set to run explicitly on the target server.



                      I'll be talking to our Admin today . . .

                      • Mail routing issue?

                        By Phillip A Kahrl 2 decades ago

                        A lot of things can go wrong when routing mail, so this issue may be related to differences between mail routing on different servers.



                        Anyone who has a problem with mail being sent should set disable mail notification from the "Email" document in the "rwfConfiguration" view. If memo documents are being created correctly, then you should look at mail routing on the server. The Email document gives you the option of sending mail directly from the application or by creating documents in "mail.box". For the "mail.box" option to work ,the signing agent must have access to the "mail.box" application.

            • Missing mail

              By Ken Hawkins 2 decades ago

              Mail doesn't seem to work for me when setup as a scheduled agent in accordance with Philips post. I don't get the log detail on my server (we have limited access to the server).



              My test results agree with Bruno's experience. It seems the code needs to run in a UI context. But this doesn't make sense for a java-based agent.



              I'm going to try triggering from another agent, but I'm not holding my breath.



              Any other ideas?

      • addendum: Edit History

        By Phillip A Kahrl 2 decades ago

        When running a scheduled/run on server agent, the document history will show the signer of the agent and not the user who changed the document. You can add the following code snippet to the end of the code in the parent, to correct the edit history.





        /// modify the history status so it shows the last user and not the server.

        if(doc.hasItem("rwfHistoryUser") && doc.getItemValue("rwfHistoryUser").size() > 0){

           Vector stats = doc.getItemValue(&quot;rwfHistoryUser&quot;);<br/>
           Vector updatedBy = doc.getItemValue(&quot;$UpdatedBy&quot;);<br/>
           stats.setElementAt(updatedBy.elementAt(updatedBy.size()-1) , stats.size()-1 );<br/>
          doc.replaceItemValue(&quot;rwfHistoryUser&quot;, stats);<br/>
        

        else{

               ///<br/>
           }
        
        • Additional code placement

          By Ken Hawkins 2 decades ago

          Adding it to the end of the "UnprocessedDocs" code results in a failure to compile. I tried including the additional code within the while loop, " while(doc != null){ ", without success. It compiles but doesn't change the history.

          • Complete agent code listing

            By Phillip A Kahrl 2 decades ago

            import lotus.domino.;

            import java.util.
            ;



            public class JavaAgent extends AgentBase {

            Database db;

            Session session;

            public void NotesMain() {<br/>
            


                try {<br/>
                    session = getSession();<br/>
                    AgentContext agentContext = session.getAgentContext();<br/>
                    db = agentContext.getCurrentDatabase();<br/>
                    View view = db.getView(&quot;(UnprocessedDocs)&quot;);<br/>
                    Vector docs = new Vector(0);<br/>
                    <br/>
                    Document doc = view.getFirstDocument();<br/>
                    while(doc != null){<br/>
                        docs.addElement(doc);<br/>
                        doc = view.getNextDocument(doc);<br/>
                        }<br/>
                                <br/>
                    for(Enumeration e=docs.elements(); e.hasMoreElements(); ){<br/>
                        doc = (Document)e.nextElement();<br/>
                        WorkflowDocument.processContextDoc(doc);<br/>
                           /// modify the history status so it shows the last user and not the server.<br/>
                            if(doc.hasItem(&quot;rwfHistoryUser&quot;) &amp;&amp; doc.getItemValue(&quot;rwfHistoryUser&quot;).size() &gt; 0){<br/>
                                Vector stats = doc.getItemValue(&quot;rwfHistoryUser&quot;);<br/>
                                Vector updatedBy = doc.getItemValue(&quot;$UpdatedBy&quot;);<br/>
                                    stats.setElementAt(updatedBy.elementAt(updatedBy.size()-1) , stats.size()-1 );<br/>
                                doc.replaceItemValue(&quot;rwfHistoryUser&quot;, stats);<br/>
                              }<br/>
                              else{<br/>
                               ///<br/>
                                }<br/>
                              doc.save();<br/>
                            }<br/>
                    <br/>
            


                } catch(Exception e) {<br/>
                    e.printStackTrace();<br/>
                }<br/>
            }<br/>
            

            }

            • thanks Phil!

              By Ken Hawkins 2 decades ago

              Thanks for the code.



              Ken

            • java 101

              By Ken Hawkins 2 decades ago

              Phil,



              First, thanks for your recent help with my questions on the rwf project. I know what a distraction side projects can be if you're in the middle of something major.



              My java skills are almost non-existent, but one of my New Year resolutions is to start using java in Domino when possible. I attempted to add the additional code for updating the rwfHistoryUser in the "while" loop. You're revision adds it in the "for" loop. From a LS perspective they both reference the doc object. How does Enumeration differentiate between the two? I'm guessing it has something to do with the WorkflowDocument.processContextDoc statement.



              Ken



              Document doc = view.getFirstDocument();

              while(doc != null){

              docs.addElement(doc);

              doc = view.getNextDocument(doc);

              }



              for(Enumeration e=docs.elements(); e.hasMoreElements(); ){

              doc = (Document)e.nextElement();

              WorkflowDocument.processContextDoc(doc);

              • Java 101 Answer, Looping

                By Phillip A Kahrl 2 decades ago

                Probably more than you want to know about Java follows, but here it is:



                There are a couple of different ways the agent could be written, and I will try to cover those here. The main thing is, we need to get a handle to each document that we want to process, the "processContextDocument" method doesn't care how we get the document handle there are several types of loops that can do this:



                The simplest is to loop through all documents in the view by using View.getNextDocument such as:



                Document doc = view.getFirstDocument()

                while(doc != null){

                  WorkflowDocument.processContextDoc(doc);<br/>
                // update the history status.<br/>
                  doc.save();<br/>
                 doc = view.getNextDocument(doc);<br/>
                

                }



                The problem with doing the loop this way, is that saving a document in the view can cause the view index to update while you are in the loop, and documents can be skipped while using the "getNextDocument" method of View.



                It is generally safer to put all the documents you want to work with first into a Vector and then loop through all elements in the Vector. The Java Vector class is the most common of the many collections that are available in Java.



                The code below puts all the documents from the View into a Vector for later use:



                Document doc = view.getFirstDocument();

                while(doc != null){

                 docs.addElement(doc);<br/>
                 doc = view.getNextDocument(doc);<br/>
                

                }



                The code uses a 'for' loop to process each document that has been added to the Vector. You could also use a while loop as follows:



                Enumeration e = docs.elements();

                while(e.hasMoreElements()){

                doc = (Document)e.nextElement();<br/>
                WorkflowDocument.processContextDoc(doc);<br/>
                /// update the history ...<br/>
                doc.save();<br/>
                

                }



                It is a much more common practice to use a 'for' loop than a 'while' loop for processing all elements in a Vector. The reason is because with the 'for' loop, the scope of the Enumeration is inside the loop where it is declared whereas for the 'while' loop the scope of the Enumeration is outside of the loop where the Enumeration is declared, and a variable such as this should use the smallest scope possible.



                The Enumeration class is an older feature in Java, and has been largely replaced by the Iterator class for newer Java code. The above code will work with the 1.1 version of Java and thus will run on an R5 server. For the R6 server, which uses JVM version 1.3, an Iterator can be used instead of an Enumeration and the preffered form of the loop would be:



                for(Iterator i=docs.iterator(); i.hasNext(); ){

                 Document doc = (Document)i.next();<br/>
                //// process the document and do all the other stuff here<br/>
                

                }



                Probably the most confusing and clunky part of the loop is the statement:



                Document doc = (Document)i.next();



                Elements in an Iterator or Enumeration are always of type Object and have to be cast to the object class that you are actually using, which means it's up to the programmer to remember what they put into the Vector. Typecasting for Vectors is a hole the strict typing that is normally a part of Java. This issue is resolved in the latest Java release (Tiger) which supports type safe Collections and Vectors. Unfortunately, it will probably be years before the newest version of Java is available in Domino.



                Hope this helps.


                • Phil
            • Server rwfHistoryUser

              By Ken Hawkins 2 decades ago

              The server name continues to appear as the user in the rwfHistoryUser field.



              What should I check?

              • History and name

                By Phillip A Kahrl 2 decades ago

                Ken,



                The agent assumes that the user name that you want to use is the last value in the "$UpdatedBy" field. This may not always be the case, if there is another field in the document that captures the user name when the document is saved, use that field instead.



                For web applications the a field that captures the "REMOTE_USER" cgi variable, works well, but I'm not sure of the best approach for Notes client.


                • Phil
                • Changed to . . .

                  By Ken Hawkins 2 decades ago

                  I changed $UpdateBy to rwfDocApproverNames. This gets the last name in the field which is typically the one I'm after.



                  Maybe if I stopped to look at the java code instead of posting . . . :)



                  Thanks.