14 Replies Latest reply: Mar 24, 2011 9:09 AM by 849418 RSS

    [Best practice] How to call a service from custom Java code

    745919
      Hi all,

      I'm wondering what the best method is to call a standard service from custom Java code?

      In a specific situation iDoc script is extended with custom functions with a custom component. There's Java code mapping to these functions that is executing these functions. The iDoc script functions are called from a workflow entry script.

      In the Java code that runs when the custom iDoc functions are called, I want to call a standard Content Server service. I don't think that the m_service variable is available, so filling the binder and using m_service.executeService() probably isn't possible.

      Also, if it were possible (that is, if I want to call a standard service from my own custom service Java code), what would then be the best method to do so?

      Regards, Stijn
        • 1. Re: [Best practice] How to call a service from custom Java code
          Swapnil Solanki
          Hey Stijn,
          If you want to call a standard service then you would need to write a Service Handler.(a service can only call another subservice, It cannot call another service). You may user executeServiceEx method there.

          cheers,
          sapan
          • 2. Re: [Best practice] How to call a service from custom Java code
            745919
            Hi Sapan,

            Let me explain a bit further.

            I'm an UCM consultant trying to solve a problem that occured at a client when they installed the CS10gR35CoreUpdateBundle.

            Content items are entered into a Workflow when they are checked in. Part of one of the entry scripts of the a workflow step is that related content to the content item in the workflow is (re)submitted for conversion.

            To achive this, a custom component provides an iDoc script extension. This iDoc function (resubmitForConversion) is implemented in Java (the class extends ScriptExtensionsAdaptor).
            In this Java method, first the related content items are fetched. Then the service RESUBMIT_FOR_CONVERSION should be called for all dID's in of the related content.

            Thus, at a certain point in the custom Java code, a native Content Server service must be called. Of course the class of this Java code does not extend the Service class, so the m_service object isn't available.

            The thing is: before installed the 10gR35CoreUpdateBundle everything worked OK. This code was used to execute the service:
                    Workspace workspace = CommonUtils.getSystemWorkspace();
                    String cmd = binder.getLocal("IdcService");
                    if (cmd == null) throw new DataException("!csIdcServiceMissing");
            
                    ServiceData serviceData = ServiceManager.getFullService(cmd);
                    if (serviceData == null) throw new DataException(LocaleUtils.encodeMessage("!csNoServiceDefined", null, cmd));
            
                    Service service = ServiceManager.createService(serviceData.m_classID, workspace, null, binder, serviceData);
            
                    UserData fullUserData = CommonUtils.getFullUserData(userName, service);
                    service.setUserData(fullUserData);
                    binder.m_environment.put("REMOTE_USER", userName);
            
                    ServiceException error = null;
                    try {
                        service.setSendFlags(true, true);
                        service.initDelegatedObjects();
                        service.globalSecurityCheck();
                        service.preActions();
                        service.doActions();
                        service.postActions();
                        service.updateSubjectInformation(true);
                        service.updateTopicInformation(binder);
                    } catch (ServiceException e) {
                        error = e;
                    } finally {
                        service.cleanUp(true);
                        if (!CommonUtils.isWorkspaceConnectionInTransaction(workspace)) {
                             workspace.releaseConnection();
                        }
                    }
            the first problem was that the CS began to complain that a transaction was started within another transaction. So I suspect that the 10gR35 update wrapped a transaction around a workflow script entry.

            With some decompiling I figured out how a service is called from iDoc with the <$executeService()$> command. So I replaced the code above with:
                          String cmd = binder.getLocal("IdcService");
                        ServiceData serviceData = ServiceManager.getFullService(cmd);
                        if (serviceData == null) throw new DataException(LocaleUtils.encodeMessage("!csNoServiceDefined", null, cmd));
                        Workspace workspace = CommonUtils.getSystemWorkspace();
                        Service service = ServiceManager.createService(serviceData.m_classID, workspace, null, binder, serviceData);
                        UserData fullUserData = CommonUtils.getFullUserData(userName, service);
                        service.setUserData(fullUserData);
                        binder.m_environment.put("REMOTE_USER", userName);
                        service.initDelegatedObjects();
                        service.executeSafeServiceInNewContext(cmd, true);
            This solved the transaction problem but introduces another problem: !csUnableToResubmitItem,(null)!csIllegalScriptAccess,RESUBMIT_FOR_CONVERSION

            The Service Reference Guide says that the access level for RESUBMIT_FOR_CONVERION is 33 (Read, Scriptable). However, in shared/config/resources/std_services.htm the access level is specified as 2 (write).


            Thus, my question still is:

            What is the best method to call a standard Content Server service from any Java code (so without extending the Service class, or having the m_service object available)?
            • 3. Re: [Best practice] How to call a service from custom Java code
              Swapnil Solanki
              Hey StijnR,
              looking at all this there are two things that are coming to my mind.
              1) change access permission of the RESUBMIT_FOR_CONVERSION service.
              2) calling your RESUBMIT_FOR_CONVERSION service from within another handler. This way context of your execution might change.

              cheers,
              sapan
              • 4. Re: [Best practice] How to call a service from custom Java code
                742311
                Hi!
                Just a guess, but I suspect that your binder is polluted. There are some valuable info about this in Bex's book.

                And since you mentioned decompiling - going through a service call is often a detour. Analyze the java methods in the service definition. You might find it easier calling them directly

                /Sam
                • 5. Re: [Best practice] How to call a service from custom Java code
                  745919
                  Hi Sapan,

                  I tried to change the access level for the service to 32 or 34, however this didn't help.
                  I think then the error changed into: !csDbUnableToStartTransaction,!csJdbcStartTranWithinATranNotAllowed.

                  I did log a SR (3-1347837571) for the fact that the Access Level of the service isn't the same as the access level mentioned in the Services Reference guide.
                  • 6. Re: [Best practice] How to call a service from custom Java code
                    745919
                    Hi Sam,

                    I also suspected this, so I tried it with this code:
                    //               Script service execution              
                                  Service service = ScriptExtensionUtils.getService(ctx, "csScriptMustBeInService");
                                  DataBinder databinder = service.getBinder();
                                  service.setBinder(binder);
                                  service.executeSafeServiceInNewContext(cmd, true);   
                    This databinder only contains:
                    <?hda version="10.1.3.3.2 (071031)" jcharset=UTF8 encoding=utf-8?>
                    @Properties LocalData
                    IdcService=RESUBMIT_FOR_CONVERSION
                    dID=<$dID$>
                    AlwaysResubmit=1
                    refineryTarget=<$refineryTarget$>
                    @end
                    No matter what code I used, the !csIllegalScriptAccess never disappeared (sometimes it was replaced with the transaction-in-a-transaction error).

                    What is the book you're mentioning? Is it only somewhere?
                    //edit: Ah, Bex Huff, I do have that book. Do you know the pagenumber of chapter?

                    Yeah, I know by now that calling a service from ordinary Java code isn't straight forward. I wish there was a good API documentation of the source code available.
                    Something like the RIDC API should also exist to be called from withing the Content Server.

                    I'll try to call the methods directly, if there's time left.

                    Edited by: StijnR on Jan 22, 2010 2:36 PM

                    Edited by: StijnR on Jan 22, 2010 2:38 PM
                    • 7. Re: [Best practice] How to call a service from custom Java code
                      742311
                      Page 234, Executing a service call from a filter
                      I know it isn't spot on, but it should give you some ideas.

                      /Sam
                      • 8. Re: [Best practice] How to call a service from custom Java code
                        745919
                        For those who might be interested:

                        I managed to create a (temporary) workaround by creating my own service: RESUBMIT_FOR_CONVERSION_NO_TRANSACTION.
                        I copied the complete service definition from RESUBMIT_FOR_CONVERSION and changed the Access Level to 34, and removed the transaction start and end control flags from the actions. This action can be called from within the Java code using the original calling construction.

                        I'm not really happy with this, as messing with transactions is bound to give you problems at some point. But hey, it's better than nothing.

                        I'm still curious to see how other people are solving the problem of trying to call (standard or custom) services from Java code. If anybody has any useful code, please post it in this thread.

                        Regards, Stijn
                        • 9. Re: [Best practice] How to call a service from custom Java code
                          765461
                          Hi Stijn,

                          I noticed you have a helper class and method: CommonUtils.isWorkspaceConnectionInTransaction(Workspace workspace)

                          How are you determining whether or not the connection in the workspace is currently in a transaction?

                          Thanks,

                          Jd
                          • 10. Re: [Best practice] How to call a service from custom Java code
                            745919
                            Here you go:


                                 /**
                                 * Checks if the connection state of the workspace is in a transaction.
                                 *
                                 * @param workspace
                                 * @returns     true if the workspace connection is in a transaction, false otherwise
                                 * @author stijn.de.reede <at> capgemini <dot> com
                                 */
                            public static boolean isWorkspaceConnectionInTransaction(Workspace workspace) {
                            Map connectionState = new HashMap();
                            workspace.getConnectionState(connectionState);
                            return Boolean.TRUE.equals(connectionState.get("isInTransaction"));
                            }

                            Edited by: StijnR on Apr 12, 2010 1:36 PM
                            • 11. Re: [Best practice] How to call a service from custom Java code
                              849418
                              Hello,
                              I need to acheive the exact same thing, call RESUBMIT_FOR_CONVERSION in workflow. Can you please share your component...Thanks
                              • 12. Re: [Best practice] How to call a service from custom Java code
                                ryan sullivan2
                                Stijn posted the details of the solution in a previous post. Have you attempted to implement?

                                Thanks,
                                -ryan

                                Ryan Sullivan | ECMconsultant
                                http://www.ecmconsultant.net/
                                • 13. Re: [Best practice] How to call a service from custom Java code
                                  745919
                                  Hi,

                                  I don't have access to this code anymore, so I cannot give you the details, but basically I created a copy of the service definition and made it scriptable, as mentioned in my previous post:
                                  "I managed to create a (temporary) workaround by creating my own service: >RESUBMIT_FOR_CONVERSION_NO_TRANSACTION.
                                  I copied the complete service definition from RESUBMIT_FOR_CONVERSION and changed the Access Level to 34, and removed the transaction start and end control flags from the actions. This action can be called from within the Java code using the original calling construction."
                                  • 14. Re: [Best practice] How to call a service from custom Java code
                                    849418
                                    Thank you, it worked, Now I can call the Service once I removed the tranaction flags (i missed that before) and made the service Scriptable.
                                    I was trying to UPDATE_DOCINFO of another content item that is not in workflow on approval of the work flowed item and also i needed to replicate that item. I was able to acheive that, but on Approval the original Content item is not moving forward in work flow. Any thoughts.....

                                    Thanks