Q&A. III

Hello , I very much love your post. I need your suggestion to current problem in my project.

We have j2ee web application and it is using DFC API to upload/download documents. Currently, we are creating a single session manager object for entire application and same will be used while creating a session but when multiple users are trying to upload documents at time than we could not creation a session and instant all threads are blocked and 1 thread is waiting to get a connection.

I’m planing to create a sessionmanager for each thread and create session from sessionmanager for each thread. Is this a correct approach? Please let us know if there is any better approach to resolve this issue.

thank you
Reddy.

Sankar, though your question is related to the final post (still unpublished 😦 ) of “Session management” series, I will share my ideas in this post. When we are thinking about how to create sessions to Content Server we should consider following problems:

  1. Session creation takes a relatively long time
  2. Sharing sessions among thread causes performance degradation
  3. A lot of opened session could overload Content Server

and there is no common approach which would be able to solve all three above problems simultaneously – everything depends on design of your application. For example, I think that the code below is the best option you can use without binding to application level:

IDfSession session = null;
IDfSessionManager sessionManager = null;
try {
    // creating brand new session manager
    sessionManager = _dfClient.newSessionManager();
    IDfLoginInfo loginInfo = new DfLoginInfo(_userName, _password);
    // preventing DFC from sending useless LOGIN RPC
    loginInfo.setForceAuthentication(false);
    sessionManager.setIdentity(_docbase, loginInfo);
    // creating brand new session
    session = sessionManager.getSession(_docbase);
 
    // some logic
 
} finally {
    if (session != null) {
        sessionManager.release(session);
        // forcibly returning session to session pool
        sessionManager.flushSessions();
    }
}

But in your case you’re playing on application level, so the possible approaches may vary, for example, you can leverage ThreadLocal capabilities to make some things more optimal, like:

ThreadLocal storage:

/**
 * @author Andrey B. Panfilov <andrew@panfilov.tel>
 */
public final class ThreadLocalStorage {

    private static final ThreadLocalStorage INSTANCE = new ThreadLocalStorage();

    private final ThreadLocal<Map<Object, Object>> _cache = new ThreadLocal<Map<Object, Object>>();

    private volatile boolean _active;

    private ThreadLocalStorage() {
        super();
    }

    public static boolean isActive() {
        return getInstance()._active;
    }

    public static void setActive(boolean active) {
        getInstance()._active = active;
    }

    private static ThreadLocalStorage getInstance() {
        return INSTANCE;
    }

    public static void clear() {
        Map cache = getCache(false);
        if (cache == null) {
            return;
        }
        for (Object value : cache.values()) {
            closeSilently(value);
        }
        cache.clear();
    }

    public static Object get(Object key) {
        Map cache = getCache(false);
        if (cache == null) {
            return null;
        }
        return cache.get(key);
    }

    public static void put(Object key, Object object) {
        Map<Object, Object> cache = getCache();
        cache.put(key, object);
    }

    public static void remove(Object key) {
        Map<Object, Object> cache = getCache();
        closeSilently(cache.remove(key));
    }

    public static boolean containsKey(String key) {
        Map<Object, Object> cache = getCache();
        return cache.containsKey(key);
    }

    private static Map<Object, Object> getCache() {
        return getCache(true);
    }

    private static Map<Object, Object> getCache(boolean create) {
        Map<Object, Object> cache = getInstance()._cache.get();
        if (create && cache == null) {
            cache = new HashMap<Object, Object>();
            getInstance()._cache.set(cache);
        }
        return cache;
    }

    private static void closeSilently(Object closeable) {
        try {
            if (!(closeable instanceof Closeable)) {
                return;
            }
            ((Closeable) closeable).close();
        } catch (IOException ex) {
            // ignore
        }
    }

}

Web filter, which maintains ThreadLocalStorage:

/**
 * @author Andrey B. Panfilov <andrew@panfilov.tel>
 */
public class ThreadLocalCacheFilter implements Filter {

    public ThreadLocalCacheFilter() {
        super();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        try {
            chain.doFilter(request, response);
        } finally {
            ThreadLocalStorage.clear();
        }
    }

    @Override
    public void destroy() {
        ThreadLocalStorage.setActive(false);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ThreadLocalStorage.setActive(true);
    }

}

SessionManager helper:

/**
 * @author Andrey B. Panfilov <andrew@panfilov.tel>
 */
public final class SessionManagerHelper {

    private SessionManagerHelper() {
        super();
    }

    public static IDfSession getSession(String docbase) throws DfException {
        return getSessionManager(docbase).getSession(docbase);
    }

    public static IDfSession newSession(String docbase) throws DfException {
        return getSessionManager(docbase).newSession(docbase);
    }

    public static void release(IDfSession session) throws DfException {
        getSessionManager(session.getDocbaseName()).release(session);
    }

    private static IDfSessionManager getSessionManager(String docbase)
        throws DfException {
        CloseableSessionManager c = (CloseableSessionManager) ThreadLocalStorage
                .get(SessionManagerHelper.class);
        if (c == null) {
            IDfSessionManager sessionManager = new DfClientX().getLocalClient()
                    .newSessionManager();
            c = new CloseableSessionManager(sessionManager);
            ThreadLocalStorage.put(SessionManagerHelper.class, c);
        }

        IDfSessionManager sessionManager = c._sessionManager;

        if (!sessionManager.hasIdentity(docbase)) {
            IDfLoginInfo loginInfo = new DfLoginInfo("user", "password");
            loginInfo.setForceAuthentication(false);
            sessionManager.setIdentity(docbase, loginInfo);
        }
        return sessionManager;
    }

    static class CloseableSessionManager implements Closeable {

        private final IDfSessionManager _sessionManager;

        public CloseableSessionManager(IDfSessionManager sessionManager) {
            _sessionManager = sessionManager;
        }

        @Override
        public void close() throws IOException {
            if (_sessionManager == null) {
                return;
            }
            _sessionManager.flushSessions();
        }

    }

}

And now your code might look like:

IDfSession session = SessionManagerHelper.getSession("docbase");
try {
    // business logic here
} finally {
    if (session != null) {
        SessionManagerHelper.release(session);
    }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s