cancel
Showing results for 
Search instead for 
Did you mean: 

CR Server 2008 / Using openDocument interface with a no-logon wrapper

Former Member
0 Kudos

Hi, all!

I had a problem with the openDocument.jsp interface and a no-logon wrapper which took me quite a while to figure out. I'm now posting these results here in the hopes that someone else will find them useful. Of course, if anyone has input how to improve the solution, it's also welcome!

The system on which this was developed and tested was a vanilla Crystal Reports Server 2008 installation on Tomcat / MySQL / Windows Server 2003.

The problem was that calls to openDocument interface left sessions open and this quickly led to the situation where all the concurrent access licenses (CALs) were used. It seemed nondeterministic when a session was released; it could have been minutes or hours.

The solution: write a HTTP session timeout listener which logoffs the CRS-backend session. (The code below has still some dubug output enabled.)


package fi.niscayah.util;

import com.crystaldecisions.sdk.framework.IEnterpriseSession;
import javax.servlet.http.*;
import java.util.Date;
import java.util.Enumeration;
import java.text.SimpleDateFormat;


public class KillSession implements HttpSessionListener
{
    public void sessionCreated(HttpSessionEvent event)
    {
        debug("sessionCreated()", event);
    }

    public void sessionDestroyed(HttpSessionEvent event)
    {
        HttpSession session = event.getSession();
        try {
            java.util.Enumeration name = session.getAttributeNames();
            while (name.hasMoreElements()) {
                String attributeName = (String)name.nextElement();
                Object attribute = session.getAttribute(attributeName);
                if((attribute != null) && (attribute instanceof IEnterpriseSession)) {
                    debug("  attribute : " + attributeName);
                    debug("  type      : " + attribute.getClass().getName());
                    IEnterpriseSession ies = (IEnterpriseSession)attribute;
                    ies.logoff();
                }
            }
            debug("sessionDestroyed()", event);
        } catch (Exception ex) {
            debug("sessionDestroyed() exception");
        }
    }
    
    private void debug(String msg)
    {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        String timestamp = sdf.format(new Date());
        
        System.err.println("[KillSession] [" + timestamp + "] " + msg);
    }
    
    private void debug(String msg, HttpSessionEvent event)
    {
        HttpSession session = event.getSession();
        String id = session.getId();
        String context = session.getServletContext().getServletContextName();
        
        debug("[" + context + "] [" + id + "] " + msg);
    }
}

(If you want to test the above code, create a .jar package out of it and put it in webapps/OpenDocument/WEB-INF/lib.)

Next we need to register our listener. I noticed that the openDocument-webapp's web.xml-file already contained a listener definition that claimed to expire enterprise sessions on HTTP timeout. I never saw such results; I tested it by registering my own listener, which only outputted debug information, and then when ever a session timeout happened, I checked the amount of licenses in use via the CMC - it never dropped predictably.

So, comment out the SessionCleanupListener and add KillSession.


<!-- SK: Added own listener. -->
<listener>
    <listener-class>fi.niscayah.util.KillSession</listener-class>
</listener>
    
<!-- SK: Commented out. -->
<!-- SessionCleanupListener is used to expire the EnterpriseSession when the web session is timeout -->    
<!-- <listener>
    <listener-class>com.businessobjects.sdk.ceutils.SessionCleanupListener</listener-class>
</listener> -->

After the above, change the HTTP session timeout to something more suitable. If you're creating really big reports, one minute might be too little. Also notice, that the value is an approximation. The timeout event might happen just as one minute has passed, but usually it takes more.


<session-config>
    <session-timeout>1</session-timeout>
</session-config>

Now we're good to go and test the openDocument interface. The result should be that every time a HTTP session timeouts, an enterprise session (which was initialized via the openDocument call) is logged off.

Next the no-logon wrapper.

I found a lot of examples for logging in automatically, but every one of them exhibited a strange behavior (at least when used in conjunction with the openDocument interface) where the session count was increased by two. A lot of head scratching later, the solution below was devised.


<%@ page language="java"
    import = "com.crystaldecisions.sdk.framework.CrystalEnterprise,
              com.crystaldecisions.sdk.framework.IEnterpriseSession,
              com.crystaldecisions.sdk.framework.ISessionMgr,
              com.crystaldecisions.sdk.exception.SDKException"
%>

<%
ISessionMgr sessionManager = CrystalEnterprise.getSessionMgr();
IEnterpriseSession entSession = sessionManager.logon("Guest", "", "<server>:6400", "secEnterprise");
String entToken = entSession.getLogonTokenMgr().createWCAToken("", 1, 1);

// So that this can be logged off when the session timeouts
HttpSession httpSession = request.getSession();
httpSession.setAttribute("nologon_SESSION", entSession);

String query = request.getQueryString();   	
String redirectURL = "http://<server>:8080/OpenDocument/opendoc/openDocument.jsp?" +
    query + "&token=" + entToken;

response.sendRedirect(redirectURL);
%>

You can put the above .jsp-file where you like, but I dropped it in webapps/openDocument, since it's no use by itself.

The use of nologon.jsp is simple: use it as you would openDocument.jsp.

And there you have it. A word of warning though, if you're not sure what you're doing, I wouldn't recommend trying these things out. And you certainly shouldn't deploy these on a production environment.

As said before, any input is welcome!

Accepted Solutions (0)

Answers (2)

Answers (2)

ted_ueda
Employee
Employee
0 Kudos

I'll comment on the BusinessObjects Enterprise logon tokens that you may generate via the Enterprise SDK.

DefaultToken - this is used for failover - i.e., if the original EnterpriseSession object is destroyed without having logoff() method invoked, the failover can be used to re-connect to Enterprise without redo-ing authentication. This token is immediately invalidated with EnterpriseSession.logoff().

CreateLogonToken - token represents an EnterpriseSession independent of the original EnterpriseSession that generates it. So you should generated the CreateLogonToken and log off the EnterpriseSession before using the token, or you'll have two licenses being used.

CreateWCAToken - the Web Component Adapter token - this token is tied to the EnterpriseSession used to create it. If this EnterpriseSession is invalidated, the WCA token can no longer be used. Since this is essentially re-use of the original EnterpriseSession, license count is not increased with its use.

So in your application, you're generating the WCA Token, and using the Session Listener to explicitly log off the originating EnterpriseSession. The SessionCleanupListener is for cleaning up sessions created within InfoView on Web Application Server Session timeout.

Sincerely,

Ted Ueda

former_member203619
Contributor
0 Kudos

Hi Samuli,

That's a nice piece of code - although there are a two things that users might want to be aware off:

1. If the report is more than 1 page and you set a short timeout - then you won't be able to view the second page since you will be logged off.

2. Similar caveat with printing, exporting and refreshing.

That being said - this is a very useful utility for those who want to ensure that their sessions are logged off in a timely manner. (And as you said - the timeout is configurable)

Shawn