Forum Stats

  • 3,839,714 Users
  • 2,262,530 Discussions
  • 7,901,048 Comments

Discussions

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

745919
745919 Member Posts: 39
edited Mar 24, 2011 10:09AM in WebCenter Content
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

Answers

  • 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
  • 745919
    745919 Member Posts: 39
    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)?
  • 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
    Swapnil Solanki
  • 742311
    742311 Member Posts: 32
    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
  • 745919
    745919 Member Posts: 39
    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.
  • 745919
    745919 Member Posts: 39
    edited Jan 22, 2010 8:38AM
    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
  • 742311
    742311 Member Posts: 32
    Page 234, Executing a service call from a filter
    I know it isn't spot on, but it should give you some ideas.

    /Sam
  • 745919
    745919 Member Posts: 39
    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
  • 765461
    765461 Member Posts: 1
    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
  • 745919
    745919 Member Posts: 39
    edited Apr 12, 2010 7:37AM
    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
This discussion has been closed.