Sunday, November 8, 2009

WebSphere Process Server JNDI client - JUnit template

Sooner or later you would like to test your code out of container. Of course you would like to use BusinessFlowManager and HumanTaskManager. To make use of them, they should be fetched from JNDI. Problem you may face is that WPS is running with security turned on. Writing JNDI client could be challenging because of JAAS. Official documentation is always missing some vital information. You end up surfing the net looking to some guidance. The same thing I did. The best one I found was blog from Nerdy Eddy. In first part he describes writing client with both SUN JRE and IBM one. To get around security check, EVERYONE group was given permission to use naming service. That will not help using BPC and HTM API. Better solution is one from second part where JAAS login is performed. You can see all the nasty exceptions you can get trying to connect. I got a many of them also. The end result for me was JUnit 4 template.
Here is the code:

import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.ibm.bpe.api.ActivityServiceTemplateData;
import com.ibm.bpe.api.BusinessFlowManager;
import com.ibm.bpe.api.BusinessFlowManagerHome;
import com.ibm.task.api.HumanTaskManager;
import com.ibm.task.api.HumanTaskManagerHome;
import com.ibm.websphere.security.auth.WSSubject;
import com.ibm.websphere.security.auth.callback.WSCallbackHandlerImpl;

/**
 * @author mresetar
 *
 */
public class SecureBfmAndHtmIntegrationTestAbstract {
    private static final Logger LOG = Logger.getLogger(SecureBfmAndHtmIntegrationTestAbstract.class.getCanonicalName());
    private static final String IIOP_URL = "iiop://localhost:2810";
    protected InitialContext ic;
    protected BusinessFlowManager bfm;
    protected HumanTaskManager htm;
    protected Subject subject;

    protected String getUsername() {
        return "admin";
    }

    protected String getPassword() {
        return "admin";
    }

    protected <T> T fetchRemoteHomeObject(Class<T> homeClass, String jndiName) throws NamingException {
        LOG.finest(String.format(
                "Trying to fetch remote home object of class %s with JNDI name %s", homeClass.getCanonicalName(), jndiName));
        final Object obj = ic.lookup(jndiName);

        return (T) PortableRemoteObject.narrow(obj, homeClass);
    }

    @Before
    public void init() throws Exception {
        Properties props = new Properties();
        props.put(InitialContext.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
        props.put(InitialContext.PROVIDER_URL, IIOP_URL);
        props.put(InitialContext.SECURITY_PRINCIPAL, getUsername());
        props.put(InitialContext.SECURITY_CREDENTIALS, getPassword());
        ic = new InitialContext(props);

        BusinessFlowManagerHome processHome = fetchRemoteHomeObject(BusinessFlowManagerHome.class,
                "com/ibm/bpe/api/BusinessFlowManagerHome");

        HumanTaskManagerHome htmHome = fetchRemoteHomeObject(HumanTaskManagerHome.class, "com/ibm/task/api/HumanTaskManagerHome");

        LoginContext lc = null;
        try {
            final WSCallbackHandlerImpl callbackHandlerImpl = new WSCallbackHandlerImpl(getUsername(), getPassword());

            lc = new LoginContext("WSLogin", callbackHandlerImpl);

        } catch (LoginException le) {
            LOG.log(Level.SEVERE, "Cannot create LoginContext.", le);
            throw le;
        } catch (SecurityException se) {
            LOG.log(Level.SEVERE, "Cannot create LoginContext.", se);
            throw se;
        }

        try {
            lc.login();
            subject = lc.getSubject();
        } catch (LoginException le) {
            LOG.log(Level.SEVERE, "Error while logging in!", le);
            throw le;
        }

        bfm = processHome.create();
        htm = htmHome.create();
    }

    @After
    public void afer() throws NamingException {
        if (ic != null) {
            ic.close();
        }
    }

    @Test
    public void testFetchWaitingActivites() throws Exception {
        WSSubject.doAs(subject, new java.security.PrivilegedAction() {
            public Object run() {
                ActivityServiceTemplateData[] waitingActivities = null;
                try {
                    waitingActivities = bfm.getWaitingActivities("_PI:90030123.e6d8e040.b8c09cf5.5dbc0000");
                    System.out.println(waitingActivities);
                } catch (Exception e) {
                    LOG.log(Level.SEVERE, "", e);
                }
                return waitingActivities;
            }
        });
    }
}


I did changes to sas.client.props and ssl.client.props. To run JUnit I had add VM arguments in Elipse's Run Configurations window to following:
-Djava.security.auth.login.config=file:///D:/IBM/WID62/pf/wps/properties/wsjaas.conf
-Dcom.ibm.CORBA.ConfigURL=file:///D:/IBM/WID62/pf/wps/properties/sas.client.props
-Djava.security.policy=file:///D:/IBM/WID62/pf/wps/properties/security.policy
-Dcom.ibm.SSL.ConfigURL=file:///D:/IBM/WID62/pf/wps/properties/ssl.client.props
-DtraceSettingsFile=TraceSettings.properties -Djava.util.logging.manager=com.ibm.ws.bootstrap.WsLogManager  -Djava.util.logging.configureByServer=true


Working directory I changed to bin (build directory) because I wanted to use TraceSettings.properties file for logging. I also changed JRE to WPS JRE. To be able to use BPC API I also added org.eclipse.equinox.registry_3.2.1.R32x_v20060814.jar from WPS plugins directory to class path.

Running the JUnit code above you should get similar to:
Processing Trace Settings File: TraceSettings.properties
[2009.11.08 22:38:00:734 CET] 0000000a WsLoggerConfi W com.ibm.ws.logging.WsLoggerConfigurator getExtensionPointLoggingConfiguration Unable to get extension point - com.ibm.wsspi.extension.logger-properties
[2009.11.08 22:38:00:906 CET] 0000000a SecureBfmAndH 3   Trying to fetch remote home object of class com.ibm.bpe.api.BusinessFlowManagerHome with JNDI name com/ibm/bpe/api/BusinessFlowManagerHome
[2009.11.08 22:38:01:484 CET] 0000000a SASRas        A   JSAS0006I: Security connection interceptor initialized.
[2009.11.08 22:38:01:828 CET] 0000000a SASRas        A   JSAS0001I: Security configuration initialized.
[2009.11.08 22:38:01:875 CET] 0000000a SSLConfig     W   CWPKI0041W: One or more key stores are using the default password.
[2009.11.08 22:38:01:875 CET] 0000000a SSLConfigMana I   CWPKI0027I: Disabling default hostname verification for HTTPS URL connections.
[2009.11.08 22:38:01:906 CET] 0000000a SASRas        A   JSAS0003I: Authentication mechanism: BASICAUTH
[2009.11.08 22:38:02:000 CET] 0000000a SASRas        A   JSAS0007I: Client request interceptor registered.
[2009.11.08 22:38:03:343 CET] 0000000a SecureBfmAndH 3   Trying to fetch remote home object of class com.ibm.task.api.HumanTaskManagerHome with JNDI name com/ibm/task/api/HumanTaskManagerHome
[2009.11.08 22:38:03:796 CET] 0000000a SecureBfmAndH E   
                                 com.ibm.bpe.api.ObjectDoesNotExistException: CWWBA0015E: The object '_PI:90030123.e6d8e040.b8c09cf5.5dbc0000' does not exist; it might have been deleted in the meantime.
 at com.ibm.bpe.framework.BusinessProcessServiceImpl.getWaitingActivities(BusinessProcessServiceImpl.java:29151)
 at com.ibm.bpe.framework.BusinessProcessServiceImpl.getWaitingActivities(BusinessProcessServiceImpl.java:29105)
 at com.ibm.bpe.framework.BusinessFlowManagerBean.getWaitingActivities(BusinessFlowManagerBean.java:16905)
 at com.ibm.bpe.api.EJSRemoteStatelessGenericBusinessFlowManagerEJB_a412961d.getWaitingActivities(Unknown Source)
 at com.ibm.bpe.api._EJSRemoteStatelessGenericBusinessFlowManagerEJB_a412961d_Tie.getWaitingActivities__CORBA_WStringValue(_EJSRemoteStatelessGenericBusinessFlowManagerEJB_a412961d_Tie.java:16226)
 at com.ibm.bpe.api._EJSRemoteStatelessGenericBusinessFlowManagerEJB_a412961d_Tie._invoke(_EJSRemoteStatelessGenericBusinessFlowManagerEJB_a412961d_Tie.java:1131)
 at com.ibm.CORBA.iiop.ServerDelegate.dispatchInvokeHandler(ServerDelegate.java:627)
 at com.ibm.CORBA.iiop.ServerDelegate.dispatch(ServerDelegate.java:480)
 at com.ibm.rmi.iiop.ORB.process(ORB.java:512)
 at com.ibm.CORBA.iiop.ORB.process(ORB.java:1571)
 at com.ibm.rmi.iiop.Connection.respondTo(Connection.java:2717)
 at com.ibm.rmi.iiop.Connection.doWork(Connection.java:2582)
 at com.ibm.rmi.iiop.WorkUnitImpl.doWork(WorkUnitImpl.java:62)
 at com.ibm.ejs.oa.pool.PooledThread.run(ThreadPool.java:118)
 at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1497)



Tuesday, November 3, 2009

Using DB2 Command Window Command to discover DB2 sqlstate message

Colleague showed me a neat way to display DB2 SQLSTATE message. For this "trick" you should have DB2 installed. Launch DB2 Command Window using shortcut or command line by typing db2cmd. New command window is opened. Then you can type db ? SQLSTATE_CODE and you should se message related to that code.
Example is:
C:\Documents and Settings\mresetar>db2 ? 20000

SQLSTATE 20000: The case was not found for the CASE statement.