Scripting Websphere 6.1 configurations, Part 7

Messaging

Part 1

Ok. Here comes the money shot. Every previous entry was just a lead up to this. What you really want to do with Websphere is create a messaging based application, if not, then why are you using it? Everything else can be done with your favorite servlet container except (really) messaging with MDB’s. Oh sure, Spring says they can do it but until I see an example of a fully transactional bean accepting a message from a iSeries MQ installation (EBCDIC -> ASCII) sucessfully using header properties and dynamic queues, I ain’t gonna believe it.

One of the biggest changes from WAS 5.1 -> WAS 6.1 was in the messaging portion of the container. You can now fully use the internal messaging provider which means you don’t have to install MQ (or whatever) on your local development machine. Another major change is they have deprecated Listener Ports (which shut down when confused) in favor of Activation Specs (basically, properties files).

Configuring a messaging based application using Jython consists of the following steps, in order:

1) Create a SI (Service Integration) Bus (Sets the name)
2) Add a SI Bus Member (Sets the properties)
3) (optional) Create MQ Client Link (Allows client connections)
4) Add Queue(s) to the SIBus
5) Add a JMS Queue Connection Factory
6) Add JMS Queues (These are tied to the SIBus Queues)
7) (optional) Add Activation Specs (Links your MDB’s to your Queues)

The next posts in the series will give examples in Jython for all the above actions. Check back or add any questions or comments.

Scripting Websphere 6.1 configurations, Part 6

Security

You want complexity? You can’t handle complexity! Seriously, you can’t or won’t want to, rather, after figuring out how to script your security settings in Websphere. The security settings in Websphere 6.1 became a lot more flexible (see: Dynamic Outbound SSL Bindings, yeah) but in turn became a lot more complicated and now of course you can’t just fill out the form like you could with Websphere 5.x (in RAD). So, let’s get started with some examples. Take and use as you see fit. The following script methods were written completely by Sony Mathew, who wishes there were examples like these when he started.

Note: Starting with the last post, I am removing all the logging statements for clarity.

###################################################################
#Security configuration using Custom FileRegistry.
###################################################################
class WASSecurityConfig:
PROP_KEY_USERS_FILE = “usersFile”;
PROP_KEY_GROUPS_FILE = “groupsFile”;

def __init__(self, wasDef):
self.wasDef = wasDef;

###########################################################
#Enables file based security given the users & groups file paths.
#Must contain “sys”, “password” as a user – which will become the system user id.
###########################################################
def enableGlobalSecurity(self, usersFilePath, groupsFilePath):

customUserRegistry = self.getCustomUserRegistryId();
if customUserRegistry == None:
raise AssertionError, “Unexpected: CustomUserRegistry configuration exists”;

if self.getUsersFilePropertyId() != None:
raise AssertionError, “Property [” + WASSecurityConfig.PROP_KEY_USERS_FILE + “] already exists”;

# Note: See previous posts for WASConfigParams object
params = WASConfigParams();
params.add(“description”, WASSecurityConfig.PROP_KEY_USERS_FILE);
params.add(“name”, WASSecurityConfig.PROP_KEY_USERS_FILE);
params.add(“value”, usersFilePath);
usersProp = AdminConfig.create(“Property”, customUserRegistry, params.asList());

if self.getGroupsFilePropertyId() != None:
raise AssertionError, “Property [” + WASSecurityConfig.PROP_KEY_GROUPS_FILE + “] already exists”;
params = WASConfigParams();
params.add(“description”, WASSecurityConfig.PROP_KEY_GROUPS_FILE);
params.add(“name”, WASSecurityConfig.PROP_KEY_GROUPS_FILE);
params.add(“value”, groupsFilePath);
groupsProp = AdminConfig.create(“Property”, customUserRegistry, params.asList());

params = WASConfigParams();
params.add(“primaryAdminId”, “sys”);
params.add(“realm”, “CustomRealm”);
params.add(“serverId”, “system”);
params.add(“serverPassword”, “password”);
params.add(“ignoreCase”, “false”);
params.add(“useRegistryServerId”, “true”);
AdminConfig.modify(customUserRegistry, params.asList());

security = self.getSecurityId();

params = WASConfigParams();
params.add(“enabled”, “true”);
params.add(“appEnabled”, “true”);
params.add(“enforceJava2Security”, “false”);
params.add(“activeUserRegistry”, customUserRegistry);
AdminConfig.modify(security, params.asList());

def getSecurityId(self):
return self.wasDef.findConfigId(“Security”, [], “Cell”);

Scripting Websphere 6.1 configurations, Part 5

Configuring the JVM and setting Websphere Variables

A typical task when deploying and managing a Websphere application is to set a Websphere variable, for example; location of property files, or an file based database. You might also want to tweak the JVM properties. For our project, we add a couple locations to our classpath, and we also set this property to help with IIOP: -Dcom.ibm.websphere.orb.uniqueServerName=true. These tasks can all be handled through scripting with Jython. Here are some examples:

def configureJVM(self, jvmProps, mode=””):

# See the previous posts on how to get the JVM ID, hint: use the AdminConfig.list() method
jvm = self.getJVMId();

genericVMArgStr = “”;
for o in jvmProps.otherJVMprops:
genericVMArgStr += ” ” + o;

classpathStr = “”;
for c in jvmProps.classpath:
if len(classpathStr) > 0:
classpathStr += “;”;
classpathStr += c;

params = WASConfigParams();
params.add(“classpath”, classpathStr);
params.add(“initialHeapSize”, jvmProps.initialHeapSize);
params.add(“maximumHeapSize”, jvmProps.maximumHeapSize);
params.add(“disableJIT”, jvmProps.disableJIT);
params.add(“genericJvmArguments”, genericVMArgStr);
AdminConfig.modify(jvm, params.asList());

The following is an example of a method that sets Websphere variables

def setVariable(self, varName, varValue, varDesc):
vmap = self.findConfigId(“VariableMap”);
params = WASConfigParams();
params.add(“symbolicName”, varName);
params.add(“value”, varValue);
params.add(“description”, varDesc);
v = AdminConfig.create(“VariableSubstitutionEntry”, vmap, params.asList());

Scripting Websphere 6.1 configurations, Part 4

Creating Datasources

In the previous post we created the datasource “Provider” which consists of telling the container where the Jar file containing your databases drivers are located. With Weblogic, you might do something as simple as adding the Jar to your server classpath when you start it up. Websphere makes it a little more complicated, but I must admin it is a little more enterprisey. You can configure the same provider jar to be a XA enabled datasource, and non-XA enabled for example.

Now it’s hopefully not too late for me to mention that you should learn all the J2C* configuration objects. I will put up a references page at the end of the series.

The next step is needed to actually use the datasource and consists of configuring the datasource and giving it a JNDI name. We put all the datasource configuration methods in to a seperate class.

########################################################################################################
# Utility class Configure a DataSource. This class contains all the createDataSourceXXX methods for all
# of our database connections. For this example I am only showing one.
########################################################################################################

class DataSourceConfig:
def __init__(self, wasDef):
self.wasDef = wasDef;
self.j2cf = J2CFactoryConfig(wasDef);

#####################################################################################################
# Method that creates a JDBC DB2 Datasource
# dsprops contains the datasource name (Which is also used to create the JNDI name) along with
# “serverName”, dsprops.server
# “portNumber”, dsprops.port
# “databaseName”, dsprops.database
# “user”, dsprops.username
# “password”, dsprops.password
#####################################################################################################
def createDataSourceDB2(self, dsprops):
log = Log(“DataSourceConfig.createDataSourceDB2”);

prov = self.getJDBCProviderDB2();
if prov == None:
raise AssertionError;

dstemplates = ListAsString(AdminConfig.listTemplates(WASConfigType.DATASOURCE, WASConfigName.DATASOURCE_DB2_UNIVERSAL)).asList();
if dstemplates == None or len(dstemplates) != 1:
raise Exception, “Too many DB2 DataSource Templates to choose from, expected only 1”;

params = WASConfigParams();
params.add(“name”, dsprops.dataSourceName);
params.add(“jndiName”, “jdbc/” + dsprops.dataSourceName);
params.add(“description”, “DB2 Universal Driver Datasource”);
params.add(“providerType”, “DB2 Universal JDBC Driver Provider”);
params.add(“authMechanismPreference”, “BASIC_PASSWORD”);
params.add(“authDataAlias”, “”);
params.add(“manageCachedHandles”, “false”);
params.add(“logMissingTransactionContext”, “true”);
params.add(“xaRecoveryAuthAlias”, “(none)”);
params.add(“diagnoseConnectionUsage”, “false”);
params.add(“statementCacheSize”, “10”);
params.add(“datasourceHelperClassname”, “com.ibm.websphere.rsadapter.DB2UniversalDataStoreHelper”);
ds = AdminConfig.createUsingTemplate(WASConfigType.DATASOURCE, prov, params.asList(), dstemplates[0]);
log.val(“DataSource Created”, ds);

self.setDataSourceProperty(ds, “serverName”, dsprops.server);
self.setDataSourceProperty(ds, “portNumber”, dsprops.port, “java.lang.Integer”);
self.setDataSourceProperty(ds, “databaseName”, dsprops.database);
self.setDataSourceProperty(ds, “user”, dsprops.username);
self.setDataSourceProperty(ds, “password”, dsprops.password);
if dsprops.schema != None:
self.setDataSourceProperty(ds, “currentSchema”, dsprops.schema);
if dsprops.driverType != None:
self.setDataSourceProperty(ds, “driverType”, dsprops.driverType);

return ds;

#returns the configId of the specified property of the specified datasource or None.
def getDataSourceProperty(self, ds, propName):
return self.j2cf.getConnectionFactoryProperty(ds, propName);

def setDataSourceProperty(self, ds, propName, propValue, propJavaType=”java.lang.String”):
return self.j2cf.setConnectionFactoryProperty(ds, “DataSource”, propName, propValue, propJavaType);

def getJDBCProviderDB2(self):
return self.wasDef.findConfigId(WASConfigType.JDBC_PROVIDER, WASConfigName.JDBC_PROVIDER_DB2_UNIVERSAL);

########################################################################################################
# Utility class to configure J2C Connection Factories of Resource Adapters.
# A Datasource is essentially like a J2CConnectionFactory and share many features but not always exact.
########################################################################################################

class J2CFactoryConfig:

def __init__(self, wasDef):
self.wasDef = wasDef;

#name: NOT JNDI name, JNDI name is composed as resource/name.
#scope: defaults to SERVER.
def getDataSource(self, name, scope=WASConfigScope.SERVER):
return self.wasDef.findConfigId(WASConfigType.DATASOURCE, name);

#name: NOT JNDI name, JNDI name is composed as resource/name.
#scope: defaults to NODE.
def getConnectionFactory(self, name, scope=WASConfigScope.NODE):
return self.wasDef.findConfigId(WASConfigType.J2C_CONNECTION_FACTORY, name, scope);

#returns the configId of the specified property of the specified Datasource or ConnectionFactory.
def getConnectionFactoryProperty(self, cf, propName):
propset = AdminConfig.showAttribute(cf, “propertySet”);
props = ListAsString(AdminConfig.list(“J2EEResourceProperty”, propset)).asList();
for prop in props:
# !!! very slow
#configPropName = AdminConfig.showAttribute(prop, “name”);
#if configPropName == propName:
# return prop;

prop = prop.lstrip();
if prop.find(propName + “(“) >=0 :
return prop;

return None;

#sets the specified property of the specified Datasource or ConnectionFactory.
#cf : configId of ConnectionFactory or DataSource.
#cfType : String identifying the type of cf – e.g. RARConnectionFactory or DataSource.
#propJavaType: All J2CConnectionFactory/Datasource properties are Java properties and their type must be given.
def setConnectionFactoryProperty(self, cf, cfType, propName, propValue, propJavaType=”java.lang.String”):
log = Log(“set” + cfType + “Property”, propName, propValue);

origProp = self.getConnectionFactoryProperty(cf, propName);
if origProp != None:
AdminConfig.remove(origProp);

propset = AdminConfig.showAttribute(cf, “propertySet”);
params = WASConfigParams();
params.add(“name”, propName);
params.add(“value”, str(propValue));
params.add(“type”, propJavaType);
params.add(“required”, “true”);
p = AdminConfig.create(“J2EEResourceProperty”, propset, params.asList());

log.done();

########################################################################################################
# Utility class to hold the internal definitions of objects.
# Note: This class is greatly expanded in real life, for this example I removed all the messaging and security stuff
########################################################################################################

class WASConfigType:
CELL = “Cell”;
NODE = “Node”
SERVER = “Server”;
CONN_POOL = “ConnectionPool”;
JDBC_PROVIDER = “JDBCProvider”;
DATASOURCE = “DataSource”;
WEB_CONTAINER = “WebContainer”;

Scripting Websphere 6.1 configurations, Part 3

Creating Datasource Providers

Probably the most common action most developers will do with any JEE container is to create and manage a Datasource. There are two steps that need to be accomplished in regards to connecting to databases. First you need to create the Datasource Provider, which mainly consists of telling the container where your JDBC Jar is located, and second you need to actually configure the datasource. I will split this example in to two posts. The first post will concentrate on the method we use to create the Provider and the second post will show you how we create the data source itself. For the examples below the helper methods/classes (You can place multiple “classes” within a single Jython file) are below.

#####################################################################################################
# Method that creates a JDBC DB2 Database Provider
# expects ${APP_PROJECTS_ROOT} WAS variable to exist.

# This provider IS NOT XA, if you need transactional support, you
# have to create an additional provider (or change XA to True)
#####################################################################################################

    def createJDBCProviderDB2(self):

        # You are logging, right?
        log = Log(“DataSourceConfig.createJDBCProviderDB2”);
       
        params = WASConfigParams();
        params.add(“name”, WASConfigName.JDBC_PROVIDER_DB2_UNIVERSAL);   
        params.add(“description”, “DB2 Universal JDBC Driver-compliant Provider.”);
        params.add(“providerType”, “DB2 Universal JDBC Driver Provider”);
        params.add(“implementationClassName”, “com.ibm.db2.jcc.DB2ConnectionPoolDataSource”);
        params.add(“xa”, “false”);       
        params.add(“classpath”, “${APP_PROJECTS_ROOT}/jdbc_drivers/db2jcc.jar;${APP_PROJECTS_ROOT}/jdbc_drivers/db2jcc_license_cu.jar”);
       
        p = AdminConfig.create(WASConfigType.JDBC_PROVIDER, self.wasDef.getServerId(), params.asList());

        # Again, make sure you are logging, Homer!
        log.val(“Created DB2 JDBC Provider”, p);

#####################################################################################################
# Simple utility to compose parameter strings or lists used for sending to wsadmin object methods.
# They are usualy of the string form “[ -nameA valueA -nameB valueB ]”
#  or of the list of lists form [[nameA,valueA],[nameB,valueB]].
#####################################################################################################

class WASConfigParams:
    def __init__(self):
        self.params = [];
   
    #returns this object.
    #specify quote=1 if u want to quote the param value.
    def add(self, paramName, paramValue, quote=0):
        if quote == 1:
            paramValue = ‘”‘ + paramValue + ‘”‘;
           
        self.params.append([paramName, paramValue]);
        return self;
   
    def asString(self):
        paramStr  = “”;
        for p in self.params:
            paramStr = paramStr + ” -” + str(p[0]) + ” ” + str(p[1]);
        paramStr = “[” + paramStr + ” ]”;
        return paramStr;
   
    def asList(self):
        return self.params;

#####################################################################################################
# Provides names of configuration items generally used. 
# These names must be of a type identified in WASConfigType.
# These are for names for configurations already existing e.g Providers. 
# For new configurations added by you – use the names you assigned.
#####################################################################################################

class WASConfigName:
    JDBC_PROVIDER_DB2_UNIVERSAL = “DB2 Universal JDBC Driver Provider”;   
    JDBC_PROVIDER_DB2_UNIVERSAL_XA = “DB2 Universal JDBC Driver Provider (XA)”;   
    DATASOURCE_DB2_UNIVERSAL = “DB2 Universal JDBC Driver DataSource”;
    DATASOURCE_DB2_UNIVERSAL_XA = “DB2 Universal JDBC Driver XA DataSource”;
    JDBC_PROVIDER_AS400_XA = “DB2 UDB for iSeries (Toolbox XA)”;
    DATASOURCE_AS400_XA = “DB2 UDB for iSeries (Toolbox XA) DataSource”;