Creating a custom profile processor requires the development of a new Java class that implements com.sas.rmi.RocfORBProfileInterface:
/** * Initializes the profile processor * @param props Properties loaded from RocfORB.properties * @return true if properly initialized */ boolean initialize(java.util.Properties props); /** * Applies the profile properties to the connection object. * @param profile The profile name * @param c The connection object. **/ void applyProfileProperties(String profile, Connection c) throws java.rmi.RemoteException; /** * Allows the processor to reload any profile information. The reload() * method is called whenever the a command is sent to the Middleware * Server to reload. The reload will only happen when there are no * active connections. */ void reload(); /** * Returns a list of the current profile names as known to the * processor. Returns null if list unavailable or unknown. * @return The list of profile names **/ String[] getProfileNames();
Once created, the new class can be specified in the property file profileProcessor key. Additional properties can be supplied to the processor in the property file by prepending 'profileProcessor' to the property name.
For example, the following values can be placed in the property file to use a sample profile processor and specify the dataset name and host name:
profileProcessor=com.sas.rmi.ExampleProfileProcessor profileProcessor.dataset=SASUSER.MWSCFG profileProcessor.dataset.connection.host=d148.us.sas.com
For more information, refer to the complete example profile processor file that follows.
The following profile processor validates a username/password and then reads a profile database.
// Copyright (c) 1998 by SAS Institute Inc., Cary, NC 27513
/**
* ExampleProfileProcessor
*
* An example webAF Middleware Server profile processor that uses
* a SAS data set. All of the username/password information is kept
* in a data set.
*
* The dataset has the following variables:
*
* USER - The username
* PASSWORD - The password
* APPLUSER - The application username
* APPLPWD - The application password
* PROPFIL - The property file name that contains all of the connection
* properties for this user
*
* The following SAS program was used to create a simple file:
*
* data sasuser.mwscfg;
* input user $20. password $ appluser $ applpwd $ propfil $30.;
* cards;
* karl.moss d148 karl karl RocfORB.profiles
* chris.bailey d133 chris chris RocfORB.profiles
* ;
* run;
*
*
* The name of the SAS dataset must be provided in the 'dataset' property which
* is supplied in the RocfORB.properties file along with the name of this
* profile processor class:
*
* # The class name of the profile processor. This class must
* # implement com.sas.rmi.RocfORBProfileInterface
* profileProcessor=com.sas.rmi.ExampleProfileProcessor
* profileProcessor.dataset=SASUSER.MWSCFG
*
*
* Other connection properties can be supplied using 'dataset' as the key:
*
* profileProcessor.dataset.connection.host=d148.us.sas.com
*
*
* The username and password given by the application will be validated against
* those given in the dataset. If no matching username/password is found an
* exception will be sent back to the client denying access. If a match is found
* the specified property file will be read and the connection properties
* will be set. The application-level username and password will also be
* set on the connection.
*
* @author Karl Moss
* @version 1.0
* @date 07Jun1999
**/
package com.sas.rmi;
import com.sas.sasserver.dataset.DataSetInterface;
public class ExampleProfileProcessor implements com.sas.rmi.RocfORBProfileInterface
{
java.util.Properties connectionProperties;
String dataSet;
java.util.Vector dataSetRows;
java.util.Hashtable propertyFiles;
public static final String CONNECTION_NAME = "connection";
public static final String CONNECTION_KEY = "dataset";
public static final String TEMP_USERNAME = "ExampleProfileProcessor.username";
public static final String TEMP_PASSWORD = "ExampleProfileProcessor.password";
public static final int USER_COLUMN = 0;
public static final int PASSWORD_COLUMN = 1;
public static final int APPLUSER_COLUMN = 2;
public static final int APPLPWD_COLUMN = 3;
public static final int PROPFIL_COLUMN = 4;
/**
* Initializes the profile processor
* @param props Properties loaded from RocfORB.properties
* @return true if loaded properly
*/
public boolean initialize(java.util.Properties props)
{
boolean rc = true;
// Look for the dataset property
dataSet = props.getProperty(CONNECTION_KEY);
if (dataSet == null) {
trace("dataset must be given");
return false;
}
// Attempt to read the dataset into memory
connectionProperties = props;
rc = load(dataSet, connectionProperties);
return rc;
}
/**
* Applies the profile properties to the connection object.
* @param profile The profile name
* @param c The connection object.
**/
public void applyProfileProperties(String profile, com.sas.rmi.Connection c)
throws java.rmi.RemoteException
{
// Make sure the data is available
if(dataSetRows == null) {
throw new java.rmi.RemoteException("User database is not available");
}
String username = c.getUsername();
String password = c.getPassword();
// Due to the fact that we may be modifying the username/password
// from the property file the original username/password
// (as set in the client) was saved with the connection. If they
// are present, use them
String orgUsername = c.getString(TEMP_USERNAME);
if (orgUsername != null) {
username = orgUsername;
password = c.getString(TEMP_PASSWORD);
}
// Get the user. Returns null if invalid
String[] data = getUser(username, password);
if (data == null) {
throw new java.rmi.RemoteException("Invalid username/password");
}
// Save the username and password before they are over-written
// by the property file
c.setString(TEMP_USERNAME, username);
c.setString(TEMP_PASSWORD, password);
// Get additional properties from the specified property file
java.util.Properties props = getProperties(data[PROPFIL_COLUMN]);
// Apply any connection properties
com.sas.rmi.ConnectionUtility.applyPropertyFile(c, props,
CONNECTION_NAME,
profile);
// Apply the application-level username and password
c.setApplicationUsername(data[APPLUSER_COLUMN]);
c.setApplicationPassword(data[APPLPWD_COLUMN]);
}
/**
* Allows the processor to reload any profile information. The reload()
* method is called whenever the a command is sent to the Middleware
* Server to reload. The reload will only happen when there are no
* active connections.
*/
public void reload()
{
// Attempt to read the dataset into memory
load(dataSet, connectionProperties);
}
/**
* Returns a list of the current profile names as known to the
* processor. Returns null if list unavailable or unknown.
* @return The list of profile names
**/
public String[] getProfileNames()
{
// The list is not available
return null;
}
/**
* Attempt to load the given dataset into memory. ROCF classes
* will be used, but the dataset could just as easily be read
* using JDBC.
* @param dataset The dataset name to read
* @param props Additional connection properties
* @return true if loaded
*/
protected boolean load(String dataset, java.util.Properties props)
{
boolean rc = true;
// Clear our internal caches
dataSetRows = null;
propertyFiles = null;
// Create a new factory
com.sas.rmi.Rocf rocf = new com.sas.rmi.Rocf();
// Create a new Connection
com.sas.rmi.Connection c = new com.sas.rmi.Connection();
// Apply any connection properties, such as dataset.connection.host etc.
com.sas.rmi.ConnectionUtility.applyPropertyFile(c, props,
CONNECTION_NAME,
CONNECTION_KEY);
try {
trace("Reading " + dataset + " from " + c.getHost());
// Attempt to create a new dataset object
DataSetInterface ds = (DataSetInterface)
rocf.newInstance(DataSetInterface.class, c);
// We've got a dataset object. Set the dataset name
ds.setDataSet(dataset);
// Validate the dataset
if (validate(ds)) {
// Read the contents of the dataset into memory
dataSetRows = new java.util.Vector();
int rows = ds.getRowCount();
for (int i = 0; i < rows; i++) {
String[] row = ds.getFormattedRow(i + 1);
// Add the row to our cache
dataSetRows.addElement(row);
}
}
}
catch (Exception ex) {
ex.printStackTrace();
rc = false;
}
finally {
// Make sure we always clean up after ourselves
rocf.stop();
}
if (rc) {
trace("Ready");
}
return rc;
}
/**
* Validates the structure of the given dataset object.
* @param ds The dataset object
* @return true if a valid structure
**/
protected boolean validate(DataSetInterface ds)
{
Object names[] = null;
try {
names = ds.getColumnLabels(1, ds.getColumnCount());
}
catch (Exception ex) {
ex.printStackTrace();
return false;
}
// Validate each column
if (!isCorrectName(names, USER_COLUMN, "USER")) return false;
if (!isCorrectName(names, PASSWORD_COLUMN, "PASSWORD")) return false;
if (!isCorrectName(names, APPLUSER_COLUMN, "APPLUSER")) return false;
if (!isCorrectName(names, APPLPWD_COLUMN, "APPLPWD")) return false;
if (!isCorrectName(names, PROPFIL_COLUMN, "PROPFIL")) return false;
return true;
}
/**
* Validates the given column name for the given index
* @param names The array of column names
* @param index The index to validate
* @param name The name to validate
* @return true if the column is the proper name
**/
protected boolean isCorrectName(Object names[], int index, String name)
{
boolean rc = true;
if (names == null) {
trace("Column names missing");
rc = false;
}
else if (names.length <= index) {
trace(name + " column is missing");
rc = false;
}
else {
String labelName = (String) names[index];
if (!labelName.equalsIgnoreCase(name)) {
trace("column at " + (index + 1) + " must be " + name);
rc = false;
}
}
return rc;
}
/**
* Output a trace statement
*/
protected void trace(String s)
{
System.out.println("ExampleProfileProcessor::" + s);
}
/**
* Validates the given username/password pair and returns the matching
* data from the dataset
* @param user The username
* @param password The user password
* @return The matching data from the dataset, or null if invalid user
*/
protected String[] getUser(String username, String password)
{
String[] data = null;
if (dataSetRows != null) {
for (int i = 0; i < dataSetRows.size(); i++) {
String[] row = (String[]) dataSetRows.elementAt(i);
// Validate the username/password
if (row[USER_COLUMN].equals(username) &&
row[PASSWORD_COLUMN].equals(password)) {
data = row;
break;
}
}
}
return data;
}
/**
* Read the properties from the given property file. An internal
* cache is used for performance
* @param The property file name
* @return The properties loaded from the file
*/
protected java.util.Properties getProperties(String fn)
{
// Create a new cache if necessary
if (propertyFiles == null) {
propertyFiles = new java.util.Hashtable();
}
// Check the cache for this file
java.util.Properties props = (java.util.Properties) propertyFiles.get(fn);
// File has not been read yet
if (props == null) {
java.io.InputStream is = null;
// Attempt to open the file
try {
is = new java.io.FileInputStream(fn);
// Load the properties
trace("Loading " + fn);
props = new java.util.Properties();
props.load(is);
}
catch (Exception ex) {
ex.printStackTrace();
// Just create an empty set of properties so we don't attempt
// to re-read the file
props = new java.util.Properties();
}
// Make sure we close the file
if (is != null) {
try {
is.close();
}
catch (Exception ex) {
}
}
// Put the properties in our cache
propertyFiles.put(fn, props);
}
return props;
}
}