Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[eclipselink-users] session handling for multi-threaded fat client

Dear all,

I have the following problem and would greatly appreciate any feedback to my
proposed solutions. I am leaning towards "Solution 2" but possibly there is
another easier way?

Thanks a lot in advance!

--
Tony 

== Problem description ==
 * Two-tiered (fat client) application with heavy DB access on the main UI
thread. 
 * Application has a low-priority background thread which needs DB access as
well

My current approach to providing DB access in the code follows and is
incorrect:
 * Once during application startup I create a DatabaseSession and keep it as
a static variable in my utility class (ApplicationDataConnector). 
 * Anywhere in the application I access this session via getSession() method
which results in different threads accessing the same session possibly at
the same time. (I did get a ConcurrentModificationException just by calling
readAllObjects())

class ApplicationDataConnector
{
    private static Session globalSession;

    public static init()
    {
        globalSession = project.createDatabaseSession();
    }

    public static Session getSession()
    {
        return globalSession;
    }
}

== Possible Solution 1 ==
Create a ServerSession and provide ClientSession instances upon request.
Unfortunately I was not able to configure the ServerSession to only open one
connection. It insists on creating a readOnlyPool in addition to a regular
pool and at a minimum creates 2 connections to the DB. In my case this
defeats the purpose as I could have just as well allocated a separate
connection to my background thread. Maintaining 2 connections is not an
option in my opinion. Any help here is appreciated. Maybe there is a way to
limit a ServerSession to one connection?  

class ApplicationDataConnector
{
    private static ServerSession globalSession;

    public static init()
    {
        globalSession = project.createServerSession();
    }

    public static Session getSession()
    {
        return globalSession.acquireClientSession();
    }
}

== Possible Solution 2 ==
Extend a DatabaseSessionImpl and manually perform synchronization, which
should not be a bottle-neck in my scenario.
For the UnitOfWork I had to copy the acquireNonSynchronizedUnitOfWork method
to inject my UnitofWork extension.
I really need some feedback in this scenario. Is it a good idea? Any
problems I am missing?

  
class ApplicationDataConnector
{
    private static SynchronizedSession globalSession;

    public static init()
    {
        globalSession = new SynchronizedSession(project);
    }

    public static Session getSession()
    {
        return globalSession
    }
}

    public static class SynchronizedSession extends DatabaseSessionImpl
    {
        public UnitOfWorkImpl acquireNonSynchronizedUnitOfWork(ReferenceMode
referenceMode) {
            setNumberOfActiveUnitsOfWork(getNumberOfActiveUnitsOfWork() +
1);
            UnitOfWorkImpl unitOfWork = new UnitOfWorkImpl(this,
referenceMode)
            {
                public void commit() throws DatabaseException,
OptimisticLockException
                {
                    synchronized(SynchronizedSession.class)
                    {
                        super.commit();    
                    }
                }

                // Override and "synchronize" more UnitOfWorkImpl methods as
needed
            };
            if (shouldLog(SessionLog.FINER, SessionLog.TRANSACTION)) {
                log(SessionLog.FINER, SessionLog.TRANSACTION,
"acquire_unit_of_work_with_argument",
String.valueOf(System.identityHashCode(unitOfWork)));
            }
            return unitOfWork;
        }

        public synchronized Vector readAllObjects(Class domainClass,
Expression selectionCriteria) throws DatabaseException
        {
            return readAllObjects(domainClass, selectionCriteria);
        }

        public synchronized Object executeQuery(DatabaseQuery query) throws
EclipseLinkException
        {
            return executeQuery(query);
        }

        // Override and "synchronize" more methods as needed

    }






Back to the top