"Access Denied" using Service.invoke() in a thread

I’m seeing a really peculiar situation here.

I have a Java service, which creates a new thread, and within that thread, calls Service.invoke(). This works fine on our development environment.

In our test environment, with exactly the same code, Service.invoke() throws a ServiceException, “[ISS.0084.9004] Access Denied”.

I found a couple of knowledgebase articles on WM Advantage, indicating that other people had the same issue. In both cases, it seems that it “should” work, but that the customer found a workaround rather than a “proper” resolution.

We’re talking WM IS 6.5 on both systems.

Any ideas?

Likely something you already thought of, but lets eliminate the obvious first.

Is the Execute ACL set to the same on the svc invoked via Service.invoke() on both systems?

Actually the non-working one is set to " (inherited)" while the working one is set to " (inherited)"

I can’t explain the discrepancy, because both were installed from the same package file. Trying with a manual change now (bear with me… we have corporate processes to slow things down…)

Changing the service on the server that doesn’t work to “inherited” - to (almost) match the " (inherited)" - did not help.

I should note that this service gets called frequently by other Java services, and it works fine. It’s when the service starts a thread and the thread uses Service.invoke() that the “Access denied” exception occurs.

I don’t have an active 6.5 available to me, but my 7.1 shows several different doInvokes. If 6.5 has the same, have you tried Service.doInvoke(NSName, Session, IData) instead of Service.doInvoke(NSName, IData)?

From a code perspective, the doInvoke that is passed the Session looks the most like the doThreadInvoke() that Advantage suggested as the workaround.

Just a guess - this one is a stumper.

Any idea what a Session is? The API docs are a little vague, but it seems to be a throwback to a data model that predates IData.

Replacing the Session.doInvoke with a Session.doThreadInvoke seems to make no difference.

… and making a Session and passing it around doesn’t help.

Come to think about it, I bet the doThreadInvoke workaround is to use doThreadInvoke instead of your own Thread.start().

Hope I don’t have to do that. It’ll be difficult.

Values objects were the in-memory data model that preceded IData.

Session was designed to be used to store state between otherwise stateless connections to the IS.

Let’s say you are at hostname A using a Firefox browser. You connect via HTTP URL to an IS for the first time to run pub.math:addFloats. A session object is created for your connection and a pipeline is created for your inputs (and use during the service.) The pipeline only sticks around for as long as it takes to execute the service and return the contents of the pipeline to the service invoker. The session object sticks around until the session timeout is reached (defaults to 10 minutes).

You can use Java services to take information from one service and stick it in the Session object, and then in another service run during a different connection, pull that information out.

All that was to answer the incidental question of what is a session. :slight_smile:

ukslim, I still can’t duplicate your problem, so I’m stuck for ideas.

OK. Well it makes no difference to the problem. I had hoped that Session was some way of maintaining authentication credentials, so perhaps by some miracle, passing the same Session from the main thread to the new thread would fix the problem.

I don’t have a IS 7, so it would be really interesting to me if someone could try this and tell me if it works there.

To reproduce:

Create a Java package “ThreadTest”.
Within ThreadTest, create a folder “ThreadTest”.
Create Java service “ThreadTest:testServiceFromThread”
Configure one input string field: “depth”

Main body of service:

  // pipeline
  IDataCursor pipelineCursor = pipeline.getCursor();
                  String    s_depth = IDataUtil.getString( pipelineCursor, "depth" );
  pipelineCursor.destroy();
   
  int depth = Integer.parseInt(s_depth);
   
  ThreadTest thread = new ThreadTest();
  thread.setDepth(depth);
  thread.start();
  thread.logExcept();
   

In “Shared”:

- Extends field: “Thread”.

In “Source” field:

  private int depth;
   
  private Exception except;
   
  public void logExcept() {
     if(except != null) {
          log("Depth = " + depth + " Exception : " + except.getMessage());
     }
  }
   
  public void setDepth(int depth) {
     this.depth = depth;
     log("ThreadTest - outside thread - depth=" + depth);
  }
   
  public void run() {
                  log("ThreadTest - in thread - depth=" + depth);
  }
   
  private void log(String log)  {
   
  // input
  IData input = IDataFactory.create();
  IDataCursor inputCursor = input.getCursor();
  IDataUtil.put( inputCursor, "message", log );
  IDataUtil.put( inputCursor, "function", "function" );
  IDataUtil.put( inputCursor, "level", "level" );
  inputCursor.destroy();
   
  // output
                                                  
  try {
     Service.doInvoke( "pub.flow", "debugLog", input );
  } catch (Exception e) {
     this.except = e;
  }
   
  }
   
  

Run the service. Input value does not matter.

What I see:
“2008-09-22 11:56:40 GMT [ISP.0090.0004C] function – ThreadTest - outside thread - depth=1” is output as exected.
“2008-09-22 11:56:40 GMT [ISP.0090.0004C] function – "ThreadTest - in thread – depth=1” is not output – failed with “Access Denied” in the ServiceException.

Also, if I substitute “run” for “start”, the problem goes away (but I’m no longer creating a thread).

Hate to be a downer, but your code worked beautifully for me. Invoked debugLog with no error. I’m on 7.1.1.

That’s not a downer at all. It indicates that WM have at some point in the past fixed this in 7.1.1. So they might know about a fix for 6.5.

Just to clarify - it invoked debugLog twice with no error, right?

Single execution. I confess, I didn’t look closely at the code - are you calling for two executions?

Ah, I see. No, the invoke I am getting is from the setDepth() call, not from the start() calling the run().

Oh well - that’s more of a bummer. It makes it less likely that the bug/feature is something Software AG can help with.

Thanks very much for your help.

No worries - good luck.

WM Support came up with a solution - it’s related to Phil’s mention of Session objects. It seems that one of the purposes of a Session is to hold authentication stuff. I guess that a Session is implicitly owned by a service’s thread, and makes it into the versions of doInvoke and doThreadInvoke that don’t take Session as an argument.

So:

  • give the Thread subclass a ‘private Session session;’
  • make a setSession() method.
  • to start the thread do:
FooThread thread = new FooThread();
thread.setSession(Service.getSession());
thread.start();

… and in the code that invokes another service within that thread:

ServiceThread st = Service.doThreadInvoke( 
     "some.path", "someService", session, input );
IData output = st.getIData();

The important thing is that the setSession is called in the caller’s thread, not in the new thread.

Incidentally, when it’s not working, at least in 6.5:

  • If you use doInvoke(), it will fail with a NullPointerException from deep in the WM code
  • If you use doThreadInvoke(), it will appear to work, but the ServiceException containing the ‘Access Denied’ error will come to light when you call getIData() on the ServiceThread.