Question

Java Access Provider: Use of Server object


Userlevel 2
Badge +5

Hi,

 

We are facing some randoms issues in our app which uses Java Access Provider. We wonder if we are using it correctly.

 

To make it short, when a  user logged in it will create a server object :

Server server = new Server();
server.setConnectionString(ifsProperties.getConnectionString());
server.setLocale("fr-FR");
server.setCredentials(username, password);

Then we put this server in a server map associated with the username. So each user have his own server object stored in the Map. When a request is made we retrieve the corresponding server in the list and use it for our request :

Server server = ifsServerPool.getServer(username);

if(server == null) {
// [...]
}

PlsqlSelectCommand cmd = new PlsqlSelectCommand(server,
String.format(
"SELECT * FROM &AO.activity_short_name_company_pa WHERE 1=1 %s ORDER BY SHORT_NAME OFFSET :OFFSET ROWS FETCH NEXT :LIMIT ROWS ONLY", searchStmt));

Record params = cmd.getBindVariables();
params.add("OFFSET", String.valueOf(offset));
params.add("LIMIT", String.valueOf(limit));

Is it the good way to use this library?

 

Thanks !

 


This topic has been closed for comments

15 replies

Userlevel 5
Badge +10

Hi Antonie, 

I am not familiar with the Java access provider, but what kind of issues you get when you have this setup? By Map do you mean the java Data structure Map?

Userlevel 2
Badge +5

Yes so for the Map it’s the Java Data Structure :

private final Map<String, Server> servers;

The error are really weird. For example when two users are connected (James and Patrick). Patrick click on a select to load activities there are properly loaded, if James click on his select, it will return a list of Patrick’s activities which doesn’t make any sense. I checked if the Server variable was the good one and yes. Backend receives James request, make a call to the weblogic via the Java Access Provider using his Server object but it returns Patrick’s activities...

 

We also have, this kind of error on random request :

ifs.fnd.ap.APException: You have entered an invalid username and/or password, or you do not have authorization to perform the requested action.

Hint: Passwords are case sensitive.
at ifs.fnd.ap.IfsClientGatewayProxy.handleHttpOther(IfsClientGatewayProxy.java:671) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.IfsClientGatewayProxy.handleHttpUnAuth(IfsClientGatewayProxy.java:479) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.IfsClientGatewayProxy.invoke(IfsClientGatewayProxy.java:471) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.IfsClientGatewayProxy.handleHttpUnAuth(IfsClientGatewayProxy.java:482) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.IfsClientGatewayProxy.invoke(IfsClientGatewayProxy.java:471) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.IfsClientGatewayProxy.invoke(IfsClientGatewayProxy.java:448) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.Server.invokeRaw(Server.java:963) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.Server.invokeBufferWithCapabilities(Server.java:784) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.Server.invokeBuffer(Server.java:834) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.Server.invokeRecord(Server.java:695) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.Server.invoke(Server.java:642) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.PlsqlCommandCollection.execute(PlsqlCommandCollection.java:119) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.PlsqlCommand.execute(PlsqlCommand.java:169) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at ifs.fnd.ap.PlsqlSelectCommand.executeQuery(PlsqlSelectCommand.java:174) ~[ap-1.0.jar:${fndext_version}-${build_no}]
at net.siinergy.service.ActivityService.getActivities(ActivityService.java:45) ~[classes/:1.0.0]

 

But if we make this request another time, 2 seconds after I may succeed which means this error is not a “real” 401. Errors are randoms. Do you have any completed examples of how to use this library?

Userlevel 5
Badge +10

Hi Antonie,

I believe you are impersonating a user like IFSADMIN to make these calls? but in the query you have written how do you filter out the specific user activity? I don’t see user-based filtering there. Since you mentioned it is showing Pattrick activities to James, I don’t think you are doing anything necessarily wrong it's just you are writing some Java code trying to access the database, I will search for any completed examples in our internal network to help you out if there’s any.

Userlevel 2
Badge +5

Thanks for your answer. I hope you could find some examples a little more complete! :)

Each user has an Oracle account so can be identified by the WebLogic. We don’t make any filtering on queries because it should be done in the WebLogic that IFS provides us and that how this is done in the clickone’s client.

 

We just tested with a colleague connecting two users retrieving their activity. I was in local (Front and back) and my back was connected to the preprod WebLogic so I could debug our back end in java. We could reproduce our error: user A sees user B’s activities but the Server sent is actually user A’s (100% sure). We look at the logs in WebLogic and for the request made by user A, web logic sees user B that’s why it returns bad activities. I don’t get how this is possible… :’( Do you have any idea?

 

Userlevel 5
Badge +10

Hi Antonie,

I found this.

import ifs.fnd.ap.*;/** * <P> * Example class for accessing PL/SQL from the Java Access Provider. * As you can see, there is no error handling in this class. A real application * must always handle the errors. This is just an easy way to avoid cluttering * the example code (since error handling isn't the topic here). * </P><P> * As you can see in the code, <B>all</B> package, table and view names must be prefixed * with '&AO.'. This is in the PL/SQL Gateway replaced with the application owner. * </P> */public class PlsqlAccess {      private Server srv;      /**    * Creates a new instance of this class.    */   public PlsqlAccess(String connectionString, String userid, String password) {      srv = new Server();            // Set the server object's connection string (where to connect to).      srv.setConnectionString(connectionString);            // Set the server object's credentials. Must be set to something real.      srv.setCredentials(userid, password);            // Set a locale (a locale (=language) must ALWAYS be used when accessing PL/SQL).      // In this case: en      srv.setLocale("en");   }      /**    * Gets a user's description by calling </code>FND_USER_API.GET_DESCRIPTION</code>.    * @param   identity the user's identity.    * @return  the user's description.    */   public String getDescription(String identity) throws APException {      // Create the PL/SQL command block. A method call inside a BEGIN END block.      PlsqlCommand cmd = new PlsqlCommand(srv, "BEGIN :DESC := &AO.FND_USER_API.GET_DESCRIPTION(:IDENTITY); END;");           // Set bind variables values and direction (a Record is used for bind variables)      Record bindVars = cmd.getBindVariables();      bindVars.add("IDENTITY", identity).setBindVariableDirection(BindVariableDirection.IN);      // The OUT-parameter don't need an initial value.      bindVars.add("DESC").setBindVariableDirection(BindVariableDirection.OUT);                  // Execute call      cmd.execute();      // Return the OUT value.      return bindVars.find("DESC").getString();         }      /**    * Gets info on a user by doing a SQL query with a bind variable.    * @param   identity the identity of the user to get the info for.    * @return  a Record with the user info. Returns null if no info is found.    */   public Record getUser(String identity) throws APException {      // The SQL query to use.      String sql = "SELECT IDENTITY,DESCRIPTION,ORACLE_USER,WEB_USER,ACTIVE FROM &AO.FND_USER WHERE IDENTITY=:IDENTITY";           // Use a PlsqlSelectCommand object for queries.      PlsqlSelectCommand selCmd = new PlsqlSelectCommand(srv, sql);                  // Set bind variable value and direction.      Record bindVars = selCmd.getBindVariables();      bindVars.add("IDENTITY", identity).setBindVariableDirection(BindVariableDirection.IN);      // Excute the query and get the result.      RecordCollection result = selCmd.executeQuery();      // See if there is a result and if any rows were returned.      if(result != null && result.size() > 0)         return result.get(0);   // Return the first row (and in this case the only one).      return null;   }      /**    * Prints out a list of all users by doing a simple SQL query.    */   public void listUsers() throws APException {      // Queries are performed with a PlsqlSelectCommand object.      PlsqlSelectCommand selCmd = new PlsqlSelectCommand(srv, "SELECT IDENTITY, DESCRIPTION FROM &AO.FND_USER");            // Excute the query and get the result.      RecordCollection result = selCmd.executeQuery();            System.out.println("All users:");            // Check if there are any rows returned.      if(result != null && result.size() > 0) {         // Iterate through the result set and print out a list of the users.         RecordIterator itr = result.recordIterator();         Record next;         while(itr.hasNext()) {            next = itr.nextRecord();            System.out.println("  " + next.findValue("IDENTITY") + "\t" + next.findValue("DESCRIPTION"));         }      }      else   // Print out a nice message if there is no result.         System.out.println("No rows selected");   }      /**    * Combines all the other operations and does them in one call to the     * PL/SQL Gateway using a PlsqlCommandCollection.    */   public void allInOne(String identity) throws APException {      // A PlsqlCommandCollection is used for executing multiple commands in one call.      PlsqlCommandCollection cmds = new PlsqlCommandCollection(srv);          // Setup the commands to execute and add them to the collection.      // Don't associate them with a Server object, since that is done on the collection instead.            PlsqlCommand getDescCmd = new PlsqlCommand("BEGIN :DESC := &AO.FND_USER_API.GET_DESCRIPTION(:IDENTITY); END;");                 getDescCmd.getBindVariables().add("IDENTITY", identity).setBindVariableDirection(BindVariableDirection.IN);      getDescCmd.getBindVariables().add("DESC").setBindVariableDirection(BindVariableDirection.OUT);            cmds.add(getDescCmd);            PlsqlSelectCommand selCmd = new PlsqlSelectCommand("SELECT IDENTITY,DESCRIPTION,ORACLE_USER,WEB_USER,ACTIVE FROM &AO.FND_USER WHERE IDENTITY=:IDENTITY");      selCmd.getBindVariables().add("IDENTITY", identity).setBindVariableDirection(BindVariableDirection.IN);            cmds.add(selCmd);            PlsqlSelectCommand listUsersCmd = new PlsqlSelectCommand("SELECT IDENTITY, DESCRIPTION FROM &AO.FND_USER");      cmds.add(listUsersCmd);            // Execute the commands      cmds.execute();            // Handle the results            System.out.println("Description for user '" + identity + "': " + getDescCmd.getBindVariables().find("DESC").getString());            System.out.println("Info on user '" + identity + "': ");      if(selCmd.getResult() != null && selCmd.getResult().size() > 0)         System.out.println(selCmd.getResult().get(0));      else         System.out.println("No user '" + identity + "' found.");            System.out.println();      System.out.println("All users:");      if(listUsersCmd.getResult() != null && listUsersCmd.getResult().size() > 0) {         RecordIterator itr = listUsersCmd.getResult().recordIterator();         Record next;         while(itr.hasNext()) {            next = itr.nextRecord();            System.out.println("  " + next.findValue("IDENTITY") + "\t" + next.findValue("DESCRIPTION"));         }      }      else         System.out.println("No rows selected");   }      /**    * Main method.    * @param args the command-line arguments to this application.    */   public static void main(String[] args) throws APException {      // Check parameters.      if(args.length != 3) {         System.err.println("Usage: PlsqlAccess <connction_string> <domain\\username> <password>");         System.exit(1);      }            // Create an instance of this object.      PlsqlAccess pa = new PlsqlAccess(args[0], args[1], args[2]);      // Call the methods.      System.out.println("Description for user 'IFSAPP': " + pa.getDescription("IFSAPP"));      System.out.println("Info on user 'IFSAPP': ");      System.out.println(pa.getUser("IFSAPP"));      pa.listUsers();      System.out.println();      System.out.println("All in one call to PL/SQL Gateway");      pa.allInOne("IFSAPP");   }}

 

Userlevel 5
Badge +10

Hi Antonie,

Seems the format/alignment is going off you can copy the code to a text editor I think it will format itself, sorry about the hassle you will have to go through reading this.

Userlevel 7
Badge +20

Hi @antoine1003 ,

 

Try using server.disconnect() after cmd.execute() to see if helps to avoid the error.

Some of my coding with Java AP can be found here. I always create the server object before the server invoke so this has not been a problem in my sight :)

https://github.com/knakit/wso2-esb-connector-ifs/tree/master/src/main/java/org/wso2/carbon/esb/connector

 

IFS guide to Java AP can be found here: Java Access Provider (ifs.com)

Java AP classdoc : ifs.fnd.ap (IFS Java Access Provider API documentation)

 

Hope it helps!

Damith

Userlevel 2
Badge +5

@infaz  Thanks for your message. I went through all the documentation and I already saw this block of code (here). It gives an idea of how to use collections, it helped me with this subject but it didn’t help a lot with the use of Server object. As @dsj suggested, I wonder if we need to close/disconnect the server after each request, this seems to be a weird utilization of a library.

Can we have multiple server Objects open at the same time? In the documentation I haven’t seen an example where server.disconnect() is called after a request? Is it the way to do it?

 

Edit: I saw in doc that they talk about a ‘OpenID’ connection system, do we need to use that?

Thanks for your help :)

Userlevel 5
Badge +10

Hi Antonie,

Maybe you can give it a shot like server.disconnect(), just like we close an Oracle cursor. Where does it say about OpenID? I believe it is a token-based authentication system largely used to communicate with Restful APIs.

Userlevel 2
Badge +5

Hey,

I tried to put server disconnect after each request and reinstanciate Server object before each request. But we still have random errors like bad credentials thrown by IFS. In the documentation here it speaks about Open ID and we wonder if we need to use that or just the credentials on server?

Userlevel 7
Badge +20

Hi @antoine1003 ,

 

Sorry to hear the problem is not completely solved but what happened to the problem of  user A sees user B’s activities, does it solved after disconnect?

 

IFS Aurena projections can be consumed with OpenID (or OAuth 2.0) is the authentication protocol as REST Apis. So if you are building an app to consume projections, you have to use OAuth2.

This is different from Access provider where authentication is handled by it’s framework and you can access IFS database objects with access providers.

If you think about future, better to start develop with REST Apis since access providers will be deprecate after apps10.

 

Hope it helps!

Damith

Userlevel 5
Badge +10

Hi Antonie, as Damith said I think it’s best to move to a client which consumes the RESTFul API’s, as from the new release which is Cloud access providers are depreciated. I am not sure how hard for you to move from your current implementation. But if you can give it a shot probably from a JS client which consumes these APIs I think it is a stable way to go. 

Userlevel 2
Badge +5

For now, we are on IFS 9 I am not sure that this API is available on this version. Is there any complete doc about this restful API with all callable url etc.. ?

 

I didn’t saw in the doc I’ve read that the Access Provider was deprecated :rolling_eyes:

Userlevel 5
Badge +10

Unfortunately, Restful APIs are exposed from Apps10 :(, I thought you are using Apps10. 

Userlevel 2
Badge +5

Okay well, we are investigating for 2 months and we still don’t have any idea of what is happening :sob: