3

I'm developing an API that connects UniVerse database to a REST endopoint. The problem is that we dont have license for native connection pooling using the UniVerse library for Java.

This software is developed in Java using Spring Boot, and the way I open sessions to universe

uniSession = uniJava.openSession();
 uniSession.setHostName(server);
 uniSession.setUserName(userName);
 uniSession.setPassword(password);
 uniSession.setAccountPath(accountPath);
 uniSession.connect(); 

I iterate over that and save sessions in a list "freeSession", when I get a request I pop a session from that list, move it to "occupiedSession", use it and return back to "freeSession".

but the problem is that in the list of sessios all are the "same". I mean, I can't recieve concurrent petitions because my API only have "one sesion" to interact with UniVerse server.

becodev
  • 41
  • 2
  • What are you using UniObjects for? Querying the database or calling BASIC subroutines? – webthaumaturge Jan 16 '22 at 23:34
  • The documentation is behind a registration for paid clients only (not a good model to support adoption of your product) so it is highly unlikely anybody here will be able to help. Does the underlying API that you use support opening multiple sessions? If that requires a license it is unlikely you can hack your way around it, and that is probably a violation of the license terms. – Jim Garrison Jan 17 '22 at 01:47
  • @webthaumaturge only for calling subroutines – becodev Jan 17 '22 at 02:28
  • How many user/normal licenses does the Universe server have? You can determine this by running uv/bin/uvlictool at command line on the Universe server. Do you only have one regular/user license? Is that what you mean by "API only has one session"? Honestly, it seems like your technique would end up with multiple parallel usable connections. – webthaumaturge Jan 17 '22 at 03:16
  • @webthaumaturge the server has aroun 300 licenses. but my api can use beetween 5 and 30. I don't have access to the server because it's of our client. My software has to connect to the universe server, call subroutines and then return the response through and REST endpoint. but the problem is how to manage and resolve concurrent petitions. I only have one set of credentials to connect to universe from my API. but right now I cant resolve more than one request at time – becodev Jan 17 '22 at 11:33
  • What version of the U2Clients, where you got the jar for UniObjects for Java, are you using? Also, what version of Universe are you connecting to? – webthaumaturge Jan 17 '22 at 13:30
  • Years ago, I had a similar problem, and ended up writing my own connection pool class. You can use a [semaphore](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html) to help with concurrency. The code that needs a unisession would borrow one from the pool, then release it in a finally block. – Tap Jan 17 '22 at 13:53
  • @webthaumaturge the jar to connect to universe UniDK Release 3.5.2 September, 2006, and the UniVerse server version is 10.1.21. thanks in advance for your help! – becodev Jan 17 '22 at 15:09
  • @Tap yes I am developing my own connection pool class... (trying haha). don't you have an example to show me? thanks – becodev Jan 17 '22 at 15:12

2 Answers2

2

Try upgrading to the latest version of UniObjects for Java in UniDK 5.2.1. I'm guessing that using UniJava may have exhibited that behavior in an older version. As long as you're just running BASIC programs, I'm guessing you will not run into any compatibility issues with 10.1.21:

  1. Visit https://rbc.rocketsoftware.com/u2bcesdinternal.asp?pid=100000047043&product=U2CL and download the U2 Current Clients installer

OR (for the long way)

  1. Visit https://rbc.rocketsoftware.com/buildmatrix.asp
  2. Select U2 Clients for the product and Windows for the platform
  3. Use the Product link in the Downloads column to download the installer

Honestly, I am using UniObjects for Java (in UniDK 5.2.1) to connect to Universe from a Spring Boot app, and I am able to create and maintain multiple parallel connections using similar code to you. If I get ten simultaneous web requests, I can see ten independent uvcs/defcs connections in Universe. The only thing I am doing differently is using the latest jars and I'm just creating a new instance of UniSession (and then later calling connect, like you) with:

UniSession uniSession = new UniSession();

instead of

UniSession uniSession = uniJava.openSession();

However, UniJava in the latest version seems to do basically the same thing as creating a new instance, but with some additional logging and tracking.

I did have someone at Rocket tell me at some point that the normal Universe license agreement forbids connection pool recreations, thus forcing you to purchase a connection pool licenses. I don't have the license handy to confirm. However, I cannot recommend purchasing connection pool licenses either without meeting with Rocket to talk specifics about your app requirements as it scales. Depending on load, durability, and performance requirements, you may need to spread read operations over multiple Universe replications, which isn't supported well/cost effectively by the published connection pool licensing model.

webthaumaturge
  • 1,198
  • 1
  • 11
  • 23
  • Thank you for your answer! I've solved the problem yesterday... I will try the latest version you shared me! I had not found those resources before – becodev Jan 18 '22 at 15:11
  • I have one more question: How can I control the sessions so they don't die due to timeout? I want to have connections available at all times and not get "The RPC failed" error when I call a subroutine. – becodev Jan 18 '22 at 19:50
2

It's been a while, and I don't have access to the code anymore, but I dealt with a similar problem years ago. The solution used a semaphore to help with the concurrency. Create a connection pool class that can be shared globally. When calling code needs a unisession, the connection pool grabs one if available, otherwise it blocks the calling thread until one becomes available. You don't want to hard code the count or the connection parameters like in this example (most of which was copied from the semaphore javadocs), of course.

import java.util.concurrent.Semaphore;

public class ConnectionPool
{
    private static final int COUNT = 5;
    private static final ConnectionPool INSTANCE = new ConnectionPool();

    private final Semaphore available = new Semaphore(COUNT, true);
    protected UniSession[] items =  new UniSession[COUNT];

    protected boolean[] used = new boolean[COUNT];
    protected UniJava uniJava = new UniJava();

    private ConnectionPool() {
        for (int i = 0; i < COUNT; i++) {
            items[i] = uniJava.openSession();
            items[i].setHostName("server name");
            items[i].setUserName("user name");
            items[i].setPassword("password");
            items[i].setAccountPath("account path");
            items[i].connect();
        }
    }

    public static ConnectionPool getInstance() {
        return INSTANCE;
    }

    public UniSession borrow() throws InterruptedException {
        available.acquire();
        return getNextAvailableItem();
    }

    public void release(UniSession session) {
        if (markAsUnused(session))
            available.release();
    }

    protected synchronized UniSession getNextAvailableItem() {
        for (int i = 0; i < COUNT; ++i) {
            if (!used[i]) {
                used[i] = true;
                return items[i];
            }
        }
        return null;
    }

    protected synchronized boolean markAsUnused(Object item) {
        for (int i = 0; i < COUNT; ++i) {
            if (item == items[i]) {
                if (used[i]) {
                    used[i] = false;
                    return true;
                } else
                    return false;
            }
        }
        return false;
    }

}

Then in your API code, when you need a unisession, you'd borrow one from the pool, making sure to release it when you're done with it. Something like this:

UniSession uniSession = null;

try {
    uniSession = ConnectionPool.getInstance().borrow();
    
    // do work
    
} finally {
    ConnectionPool.getInstance().release(uniSession);
}

This lets your application reuse a pool of sessions, and the number of sessions never exceeds the count you specify.

Tap
  • 6,332
  • 3
  • 22
  • 25