Sunday, April 1, 2012

Mock Weblogic Login module - Identity Asserter and Authenticator

This module is strictly for testing as it does not validate user.

Based on this log http://weblogic-wonders.com/weblogic/2010/03/15/custom-identity-asserter-for-weblogic-server/

Oracle® Fusion Middleware Developing Security Providers for Oracle WebLogic Server
http://docs.oracle.com/cd/E14571_01/web.1111/e13718/atn.htm#i115061
Identity Assertion Providers
http://docs.oracle.com/cd/E14571_01/web.1111/e13718/ia.htm#i1156163


Steps to create login module
1. Create MBean definition file SimpleSampleIdentityAsserter.xml
<?xml version="1.0" ?>
<!DOCTYPE MBeanType SYSTEM "commo.dtd">

<!-- MBean Definition File (MDF) for the Simple Sample Identity Asserter.

Copyright (c) 2002 by BEA Systems, Inc. All Rights Reserved.
-->

<!-- Declare your mbean.

Since it is for an identity asserter, it must extend the
weblogic.management.security.authentication.IdentityAsserter mbean.

The Name and DisplayName must be the same.
They specify the name that will appear on the
console for this provider.

Set the PeristPolicy to "OnUpdate" so that if an attribute
value is changed, the new value is written to disk immediately.
See the "Developing Security Services" manual for more info.

Note that since this is an xml document, you can't use double
quotes directly. Instead you need to use &quot;

Note that setting "Writeable" to "false" on an attribute
makes the attribute read-only. The default is read-write.
-->

<MBeanType
Name = "SimpleSampleIdentityAsserter"
DisplayName = "SimpleSampleIdentityAsserter"
Package = "examples.security.providers.identityassertion.simple"
Extends = "weblogic.management.security.authentication.IdentityAsserter"
PersistPolicy = "OnUpdate"
>

<!-- You must set the value of the ProviderClassName attribute
(inherited from the weblogic.management.security.Provider mbean)
to the name of the java class you wrote that implements the
weblogic.security.spi.AuthenticationProvider interface.

You can think of the provider's mbean as the factory
for your provider's runtime implementation.
-->
<MBeanAttribute
Name = "ProviderClassName"
Type = "java.lang.String"
Writeable = "false"
Preprocessor = "weblogic.management.configuration.LegalHelper.checkClassName(value)"
Default = "&quot;examples.security.providers.identityassertion.simple.SimpleSampleIdentityAsserterProviderImpl&quot;"
/>

<!-- You must set the value of the Description attribute
(inherited from the weblogic.management.security.Provider mbean)
to a brief description of your provider.
It is displayed in the console.
-->
<MBeanAttribute
Name = "Description"
Type = "java.lang.String"
Writeable = "false"
Default = "&quot;WebLogic Simple Sample Identity Asserter Provider&quot;"
/>

<!-- You must set the value of the Version attribute
(inherited from the weblogic.management.security.Provider mbean)
to your provider's version. There is no required format.
-->
<MBeanAttribute
Name = "Version"
Type = "java.lang.String"
Writeable = "false"
Default = "&quot;1.0&quot;"
/>

<!-- You must set the value of the SupportedTypes attribute
(inherited from the
weblogic.management.security.authentication.IdentityAsserter mbean)
to the list of token types that your identity asserter supports.

Whoever is initiating the identity assertion (eg. a client sending
a perimeter authentication token via an HTTP request header), must
use the same token type.
-->
<MBeanAttribute
Name = "SupportedTypes"
Type = "java.lang.String[]"
Writeable = "false"
Default = "new String[] { &quot;SamplePerimeterAtnToken&quot; }"
/>
<!-- The ActiveTypes attribute (a settable attribute inherited from the
weblogic.management.security.authentication.IdentityAsserter mbean)
contains the subset of your mbean's SupportedTypes that are active
in the realm.

Which way you should default the active types attribute depends
on your token types. The basic rule is that for any token
type, there must only be one identity asserter in the realm with
that token type as an active type. In short, you can only have
one identity asserter turned on for a given type.

If your token types are commonly implemented by other identity
asserters (eg. X509 certificates), then you should not set them
as default active types. Otherwise, it would be very easy for
an administrator to configure an invalid realm where more than
one identity asserter has the same type turned on. Best
practice is that all the identity asserters turn off the type
by default then the administrator manually turns on the
type in one of the identity asserters that support it.
Look at the weblogic.security.spi.IdentityAsserter javadoc
for some standard token types.

On the other hand, if you have a custom token type that no
other identity asserter will ever implement, you may default the
active types attribute to include your token type. If you do,
then the adminstrator doesn't have to manually turn on your token
type.

Since the simple sample identity asserter's token type is very specific
to the sample (instead of a common type like X509), turn on the
token type by default.
-->
<MBeanAttribute
Name = "ActiveTypes"
Type = "java.lang.String[]"
Default = "new String[] { &quot;SamplePerimeterAtnToken&quot; }"
/>

<!-- Add any custom attributes for your provider here.

The simple sample identity asserter does not have any custom attributes.

Note: custom attributes do not appear in the
console in WLS 7.0. Use the admin command line tool
(java weblogic.Admin) to view and set their values.

Refer to the "Developing Security Services" manual
for more info on defining custom attributes.
-->

<MBeanAttribute
Name = "Base64DecodingRequired"
Type = "boolean"
Writeable = "false"
Default = "false"
Description = "See MyIdentityAsserter-doc.xml."
/>

</MBeanType>



2. copy commo.dtd from WLS installation
[dave@dave src]$ find /app/weblogic121/ -name commo.dtd
/app/weblogic121/wlserver_12.1/server/lib/commo.dtd
/app/weblogic121/wlserver_12.1/server/lib/mbeantypes/commo.dtd
[dave@dave src]$ cp /app/weblogic121/wlserver_12.1/server/lib/mbeantypes/commo.dtd .




3. modify classes for IdentityAsserter, LoginModule and CallbackHandler - only 3 classes have to be provided , rest is generated
[dave@dave src]$ ls -1
build.xml
commo.dtd
SimpleSampleCallbackHandlerImpl.java
SimpleSampleIdentityAsserterImpl.java
SimpleSampleIdentityAsserter.jar
SimpleSampleIdentityAsserterProviderImpl.java
SimpleSampleIdentityAsserter.xml
SimpleSampleLoginModuleImpl.java
test



This is main Provider class which drives the process SimpleSampleIdentityAsserterProviderImpl.
It provides IdentityAsserter and it calls SimpleSampleLoginModule
package examples.security.providers.identityassertion.simple;

import java.util.HashMap;

import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;

import weblogic.management.security.ProviderMBean;
import weblogic.security.provider.PrincipalValidatorImpl;
import weblogic.security.service.ContextHandler;
import weblogic.security.spi.AuthenticationProviderV2;
import weblogic.security.spi.IdentityAsserterV2;
import weblogic.security.spi.IdentityAssertionException;
import weblogic.security.spi.PrincipalValidator;
import weblogic.security.spi.SecurityServices;

/**
* The simple sample identity asserter's runtime implementation.
*
* It looks for tokens of type "SamplePerimeterAtnToken"
* whose matching token is an array of bytes containing a
* string in the form "username=someusername".
*
* It extracts the username from the token and stores it
* in a SimpleSampleCallbackHandlerImpl. This is returned to the
* security framework who hands it to the authenticators'
* login modules. The login modules can use a NameCallback
* to retrieve the user name from the simple sample identity
* asserter's callback handler.
*
* Since it is an identity asserter, it must implement
* the weblogic.security.spi.AuthenticationProvider and
* the weblogic.security.spi.IdentityAsserter interfaces.
*
* It can either implement two classes, and use the
* provider implementation as the factory as the
* factory for the identity asserter, or it can implement
* both interfaces in one class. The simple sample identity
* asserter implments both interfaces in one class.
*
* Note: The simple sample identity asserter's mbean's ProviderClassName
* attribute must be set the the name of this class.
*
* @author Copyright (c) 2002 by BEA Systems. All Rights Reserved.
*/
public final class SimpleSampleIdentityAsserterProviderImpl implements AuthenticationProviderV2, IdentityAsserterV2
{
final static private String TOKEN_TYPE = "SamplePerimeterAtnToken"; // the kind of token's we handle
final static private String TOKEN_PREFIX = "username="; // the token contains a string in the form "username=someusername"

private String description; // a description of this provider
private LoginModuleControlFlag controlFlag; // how this provider's login module should be used during the JAAS login


/**
* Initialize the simple sample identity asserter.
*
* @param mbean A ProviderMBean that holds the simple sample identity asserter's
* configuration data. This mbean must be an instance of the simple sample
* identity asserter's mbean.
*
* @param services The SecurityServices gives access to the auditor
* so that the provider can to post audit events.
* The simple sample role mapper doesn't use this parameter.
*
* @see SecurityProvider
*/
public void initialize(ProviderMBean mbean, SecurityServices services)
{
System.out.println("SimpleSampleIdentityAsserterProviderImpl.initialize");
SimpleSampleIdentityAsserterMBean myMBean = (SimpleSampleIdentityAsserterMBean)mbean;
description = myMBean.getDescription() + "\n" + myMBean.getVersion();

controlFlag = LoginModuleControlFlag.SUFFICIENT;
/* String flag = myMBean.getControlFlag();
if (flag.equalsIgnoreCase("REQUIRED")) {
controlFlag = LoginModuleControlFlag.REQUIRED;
} else if (flag.equalsIgnoreCase("OPTIONAL")) {
controlFlag = LoginModuleControlFlag.OPTIONAL;
} else if (flag.equalsIgnoreCase("REQUISITE")) {
controlFlag = LoginModuleControlFlag.REQUISITE;
} else if (flag.equalsIgnoreCase("SUFFICIENT")) {
controlFlag = LoginModuleControlFlag.SUFFICIENT;
} else {
throw new IllegalArgumentException("invalid flag value" + flag);
}*/
}

/**
* Get the simple sample identity asserter's description.
*
* @return A String containing a brief description of the simple sample identity asserter.
*
* @see SecurityProvider
*/
public String getDescription()
{
return description;
}

/**
* Shutdown the simple sample identity asserter.
*
* A no-op.
*
* @see SecurityProvider
*/
public void shutdown()
{
System.out.println("SimpleSampleIdentityAsserterProviderImpl.shutdown");
}

/**
* Gets the simple sample identity assertion provider's identity asserter object.
*
* @return The simple sample identity assertion provider's IdentityAsserter object.
*
* @see AuthenticationProvider
*/
public IdentityAsserterV2 getIdentityAsserter()
{
return this;
}

/**
* Assert identity given a token that identifies the user.
*
* @param type A String containing the token type. The simple sample identity
* asserter only supports tokens of type "SamplePerimeterAtnToken".
* Also, the simple sample identity asserter's mbean's "ActiveTypes" attribute
* must be set to "SamplePerimeterAtnToken" (which is done by default
* when the mbean is created).
*
* @param token An Object containing the token that identifies the user.
* The simple sample identity asserter's token must be an array of bytes
* containing a String of the form "username=someusername".
*
* @param handler A ContextHandler object that can optionally
* be used to obtain additional information that may be used in
* asserting the identity. If the caller is unable to provide additional
* information, a null value should be specified. This sample
* ignores the handler.
*
* While, for simplicity, this sample does not validate the
* contents of the token, identity asserters typically should do
* this (to prevent someone from forging a token). For
* example, when using Kerberos, the token may be generated
* and "signed" by a Kerberos server and the identity asserter
* hands the token back to the Kerberos server to get it
* validated. Another example: when asserting identity from
* X509 certificates, then identity asserter should validate the
* certificate - that it hasn't been tampered, that it's been
* signed by a trusted CA, that it hasn't expired or revoked, etc.
*
* @return a CallbackHandler that stores the username from the token.
* The username can only be retrieved from the callback handler by
* passing in a NameCallback. The sample returns an instance of
* its CallbackHandler implementation (SimpleSampleCallbackHandlerImpl).
*
* @throws IdentityAssertionException if another token type is passed
* in or the token doesn't have the correct form.
*/
public CallbackHandler assertIdentity(String type, Object token, ContextHandler context) throws IdentityAssertionException
{
System.out.println("SimpleSampleIdentityAsserterProviderImpl.assertIdentity");
System.out.println("\tType\t\t= " + type);
System.out.println("\tToken\t\t= " + token);

// check the token type
if (!(TOKEN_TYPE.equals(type))) {
String error =
"SimpleSampleIdentityAsserter received unknown token type \"" + type + "\"." +
" Expected " + TOKEN_TYPE;
System.out.println("\tError: " + error);
throw new IdentityAssertionException(error);
}

// make sure the token is an array of bytes
if (!(token instanceof byte[])) {
String error =
"SimpleSampleIdentityAsserter received unknown token class \"" + token.getClass() + "\"." +
" Expected a byte[].";
System.out.println("\tError: " + error);
throw new IdentityAssertionException(error);
}

// convert the array of bytes to a string
byte[] tokenBytes = (byte[])token;
if (tokenBytes == null || tokenBytes.length < 1) {
String error =
"SimpleSampleIdentityAsserter received empty token byte array";
System.out.println("\tError: " + error);
throw new IdentityAssertionException(error);
}

String tokenStr = new String(tokenBytes);

// make sure the string contains "username=someusername
if (!(tokenStr.startsWith(TOKEN_PREFIX))) {
String error =
"SimpleSampleIdentityAsserter received unknown token string \"" + type + "\"." +
" Expected " + TOKEN_PREFIX + "username";
System.out.println("\tError: " + error);
throw new IdentityAssertionException(error);
}

// extract the username from the token
String userName = tokenStr.substring(TOKEN_PREFIX.length());
System.out.println("\tuserName\t= " + userName);

// store it in a callback handler that authenticators can use
// to retrieve the username.
return new SimpleSampleCallbackHandlerImpl(userName);
}

/**
* Create a JAAS AppConfigurationEntry (which tells JAAS
* how to create the login module and how to use it) when
* the simple sample authenticator is used to authenticate (vs. to
* complete identity assertion).
*
* @return An AppConfigurationEntry that tells JAAS how to use the simple sample
* authenticator's login module for authentication.
*/
public AppConfigurationEntry getLoginModuleConfiguration()
{
// Don't pass in any special options.
// By default, the simple sample authenticator's login module
// will authenticate (by checking that the passwords match).
HashMap options = new HashMap();
return getConfiguration(options);
}

/**
* Create a JAAS AppConfigurationEntry (which tells JAAS
* how to create the login module and how to use it).
* This helper method is used both for authentication mode
* and identity assertion mode.
*
* @param options A HashMap containing the options to pass to the
* simple sample authenticator's login module. This method adds the
* "database helper" object to the options. This allows the
* login module to access the users and groups.
*
* @return An AppConfigurationEntry that tells JAAS how to use the simple sample
* authenticator's login module.
*/
private AppConfigurationEntry getConfiguration(HashMap options)
{
System.out.println("SimpleSampleIdentityAsserterProviderImpl: getConfiguration");
// make sure to specify the simple sample authenticator's login module
// and to use the control flag from the simple sample authenticator's mbean.
return new
AppConfigurationEntry(
"examples.security.providers.authentication.simple.SimpleSampleLoginModuleImpl",
controlFlag,
options
);
}

/**
* Create a JAAS AppConfigurationEntry (which tells JAAS
* how to create the login module and how to use it) when
* the simple sample authenticator is used to complete identity
* assertion (vs. to authenticate).
*
* @return An AppConfigurationEntry that tells JAAS how to use the simple sample
* authenticator's login module for identity assertion.
*/
public AppConfigurationEntry getAssertionModuleConfiguration()
{
System.out.println("SimpleSampleIdentityAsserterProviderImpl: getAssertionModuleConfiguration");
// Pass an option indicating that we're doing identity
// assertion (vs. authentication) therefore the login module
// should only check that the user exists (instead of checking
// the password)
HashMap options = new HashMap();
options.put("IdentityAssertion","true");
return getConfiguration(options);
}

/**
* Return the principal validator that can validate the
* principals that the authenticator's login module
* puts into the subject.
*
* Since the simple sample authenticator uses the built in
* WLSUserImpl and WLSGroupImpl principal classes, just
* returns the built in PrincipalValidatorImpl that knows
* how to handle these kinds of principals.
*/
public PrincipalValidator getPrincipalValidator()
{
return new PrincipalValidatorImpl();
}
}



SimpleSampleLoginModule - set as SUFFICIENT

package examples.security.providers.authentication.simple;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.spi.LoginModule;
import weblogic.management.utils.NotFoundException;
import weblogic.security.principal.WLSGroupImpl;
import weblogic.security.principal.WLSUserImpl;

/**
* The simple sample authenticator's login module implementation.
*
* It is used in one of two modes:
* - authentication where it validates the user's password
* then populates the subject with the user and the user's groups.
* - identity assertion where it checks that the user exists,
* then populates the subject with the user and the user's groups.
*
* The SimpleSampleAuthenticationProviderImpl creates an options hash map
* that is passed to this login module. It contains one entry,
* named "database", that is an object that manages the
* user and group definitions. It optionally contains another entry,
* named "IdentityAssertion", that puts the login module in
* "identity assertion" mode (vs. the default which is "authenticadtion"
* mode).
*
* It uses the built in WLSUserImpl and WLSGroupImpl classes to
* populate the subject with users and groups.
*
* @author Copyright (c) 2002 by BEA Systems. All Rights Reserved.
*/
final public class SimpleSampleLoginModuleImpl implements LoginModule
{
private Subject subject; // the subject for this login
private CallbackHandler callbackHandler; // where to get user names, passwords, ... for this login

private boolean isIdentityAssertion; // are we in authentication or identity assertion mode?

// Authentication status
private boolean loginSucceeded; // have we successfully logged in?
private boolean principalsInSubject; // did we add principals to the subject?
private Vector principalsForSubject = new Vector(); // if so, what principals did we add to the subject
// (so we can remove the principals we added if the login is aborted)
/**
* Initialize a login attempt.
*
* @param subject the Subject this login attempt will populate.
*
* @param callbackhandler the CallbackHandler that can be used to
* get the user name, and in authentication mode, the user's password
*
* @param sharedState A Map containing data shared between login
* modules when there are multiple authenticators configured. This
* simple sample does not use this parameter.
*
* @param options A Map containing options that the authenticator's
* authentication provider impl wants to pass to its login module impl.
* For example, it can be used to pass in configuration data (where
* is the database holding user and group info) and to pass in whether
* the login module is used for authentication or to complete identity
* assertion.
* The SimpleSampleAuthenticationProviderImpl adds an option named "database".
* The value is a SimpleSampleAuthenticatorDatabase object. It gives the
* login module access to the user and group definitions.
* When the authenticator is being used in identity assertion mode,
* the SimpleSampleAuthenticationProviderImpl also adds an option named
* "IdentityAssertion". It indicates that the login module should only
* verify that the user exists (vs. checking the password too). If
* this option is not specified (or is set to false), then the
* login module checks the user's password too (that is, it assumes
* authentication mode).
*/
public void
initialize(
Subject subject,
CallbackHandler callbackHandler,
Map sharedState,
Map options
)
{
// only called (once!) after the constructor and before login

System.out.println("SimpleSampleLoginModuleImpl.initialize");
this.subject = subject;
this.callbackHandler = callbackHandler;

// Determine if we're in identity assertion or authentication mode
isIdentityAssertion =
"true".equalsIgnoreCase((String)options.get("IdentityAssertion"));

}

/**
* Attempt to login.
*
* If we're in authentication mode, extract the user name and password
* from the callback handler. If the user exists and the password matches,
* then populate the subject with the user and the user's group. Otherwise,
* the login fails.
*
* If we're in identity assertion mode, extract the user name (only)
* from the callback handler. If the user exists, then populate the
* subject with the user and the user's groups. Otherwise, the
* login fails.
*
* @return A boolean indicating whether or not the login for
* this login module succeeded.
*/
public boolean login() throws LoginException
{
// only called (once!) after initialize

System.out.println("SimpleSampleLoginModuleImpl.login");

// loginSucceeded should be false
// principalsInSubject should be false

// Call a method to get the callbacks.
// For authentication mode, it will have one for the
// username and one for the password.
// For identity assertion mode, it will have one for
// the user name.
Callback[] callbacks = getCallbacks();

// Get the user name.
String userName = getUserName(callbacks);

if (userName.length() > 0) {
// We have a user name
System.out.println("\tuserName=" + userName);
} else {
// anonymous login.
System.out.println("\tempty userName");
}

loginSucceeded = true;

// since the login succeeded, add the user and its groups to the
// list of principals we want to add to the subject.
principalsForSubject.add(new WLSUserImpl(userName));
addGroupsForSubject(userName);

return loginSucceeded;
}

/**
* Completes the login by adding the user and the user's groups
* to the subject.
*
* @return A boolean indicating whether or not the commit succeeded.
*/
public boolean commit() throws LoginException
{
// only called (once!) after login

// loginSucceeded should be true or false
// principalsInSubject should be false
// user should be null if !loginSucceeded, null or not-null otherwise
// group should be null if user == null, null or not-null otherwise

System.out.println("SimpleSampleLoginModule.commit");
if (loginSucceeded) {
// put the user and the user's groups (computed during the
// login method and stored in the principalsForSubject object)
// into the subject.
subject.getPrincipals().addAll(principalsForSubject);
principalsInSubject = true;
return true;
} else {
return false;
}
}

/**
* Aborts the login attempt. Remove any principals we put
* into the subject during the commit method from the subject.
*
* @return A boolean indicating whether or not the abort succeeded.
*/
public boolean abort() throws LoginException
{
// only called (once!) after login or commit
// or may be? called (n times) after abort

// loginSucceeded should be true or false
// principalsInSubject should be false if user is null, otherwise true or false

System.out.println("SimpleSampleLoginModule.abort");
if (principalsInSubject) {
subject.getPrincipals().removeAll(principalsForSubject);
principalsInSubject = false;
}
return true;
}

/**
* Logout. This should never be called.
*
* @return A boolean indicating whether or not the logout succeeded.
*/
public boolean logout() throws LoginException
{
// should never be called

System.out.println("SimpleSampleLoginModule.logout");
return true;
}

/**
* Throw an invalid login exception.
*
* @param msg A String containing the text of the LoginException.
*
* @throws LoginException
*/
private void throwLoginException(String msg) throws LoginException
{
System.out.println("Throwing LoginException(" + msg + ")");
throw new LoginException(msg);
}

/**
* Throws a failed login excception.
*
* @param msg A String containing the text of the FailedLoginException.
*
* @throws LoginException
*/
private void throwFailedLoginException(String msg) throws FailedLoginException
{
System.out.println("Throwing FailedLoginException(" + msg + ")");
throw new FailedLoginException(msg);
}

/**
* Get the list of callbacks needed by the login module.
*
* @return The array of Callback objects by the login module.
* Returns one for the user name and password if in authentication mode.
* Returns one for the user name if in identity assertion mode.
*/
private Callback[] getCallbacks() throws LoginException
{
if (callbackHandler == null) {
throwLoginException("No CallbackHandler Specified");
}

Callback[] callbacks;
if (isIdentityAssertion) {
callbacks = new Callback[1]; // need one for the user name
} else {
callbacks = new Callback[2]; // need one for the user name and one for the password

// add in the password callback
callbacks[1] = new PasswordCallback("password: ",false);
}

// add in the user name callback
callbacks[0] = new NameCallback("username: ");

// Call the callback handler, who in turn, calls back to the
// callback objects, handing them the user name and password.
// These callback objects hold onto the user name and password.
// The login module retrieves the user name and password from them later.
try {
callbackHandler.handle(callbacks);
} catch (IOException e) {
throw new LoginException(e.toString());
} catch (UnsupportedCallbackException e) {
throwLoginException(e.toString() + " " + e.getCallback().toString());
}

return callbacks;
}

/**
* Get the user name from the callbacks (that the callback handler
* has already handed the user name to).
*
* @param callbacks The array of Callback objects used by this login module.
* The first in the list must be the user name callback object.
*
* @return A String containing the user name (from the user name callback object)
*/
private String getUserName(Callback[] callbacks) throws LoginException
{
String userName = ((NameCallback)callbacks[0]).getName();
if (userName == null) {
throwLoginException("Username not supplied.");
}
System.out.println("\tuserName\t= " + userName);
return userName;
}

/**
* Add the user's groups to the list of principals to be added to the subject.
*
* @param A String containing the user name the user's name.
*/
private void addGroupsForSubject(String userName)
{
// Get the user's list of groups (recursively - so, if user1 is a member
// of group1 and group1 is a member of group2, then it returns group1 and
// group2). Iterate over the groups, adding each to the list of principals
// to add to the subject.
String groupName = "SamplePerimeterAtnUsers";
System.out.println("\tgroupName\t= " + groupName);
principalsForSubject.add(new WLSGroupImpl(groupName));
}

/**
* Get the password from the callbacks (that the callback handler
* has already handed the password to) - that is, the password from
* the login attempt. Must only be used for authentication mode, not
* for identity assertion mode.
*
* @param useName A String containing the name of the user
* (already retrieved from the callbacks). Only passed in
* so that we can print a better error message if the password
* is bogus.
*
* @param callbacks The array of Callback objects used by this login module.
* The second in the list must be the password callback object.
*
* @return A String containing the password from the login attempt
*
* @throws LoginException if no password was supplied in the login attempt.
*/
private String getPasswordHave(String userName, Callback[] callbacks) throws LoginException
{
PasswordCallback passwordCallback = (PasswordCallback)callbacks[1];
char[] password = passwordCallback.getPassword();
passwordCallback.clearPassword();
if (password == null || password.length < 1) {
throwLoginException("Authentication Failed: User " + userName + ". Password not supplied");
}
String passwd = new String(password);
System.out.println("\tpasswordHave\t= " + passwd);
return passwd;
}
}



CallbackHandler
package examples.security.providers.identityassertion.simple;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

/**
* The simple sample identity asserter's implementation of the
* CallbackHandler interface.
*
* It is used to make the name of the user from the identity
* assertion token available to the authenticators (who, in
* turn, will populate the subject with the user and the user's
* groups).
*
* This class is internal to the simple sample identity asserter.
* It is not a public class.
*
* @author Copyright (c) 2002 by BEA Systems. All Rights Reserved.
*/
/*package*/ class SimpleSampleCallbackHandlerImpl implements CallbackHandler
{
private String userName; // the name of the user from the identity assertion token

/**
* Create a callback handler that stores the user name.
*
* @param user A String containing the name of the user
* from the identity assertion token
*/
/*package*/ SimpleSampleCallbackHandlerImpl(String user)
{
userName = user;
}

/**
* Used by the authenticators' login modules to get the user name
* that the identity asserter extracted from the identity assertion token.
* This name can only be retrieved via a NameCallback.
*
* @param callbacks An array of Callback objects indicating what data
* the login module is trying to extract from this callback handler.
* It must only contain NameCallbacks.
*
* @exception UnsupportedCallbackException thrown if any of the callbacks
* aren't NameCallbacks.
*
* @see CallbackHandler
*/
public void handle(Callback[] callbacks) throws UnsupportedCallbackException
{
// loop over the callbacks
for (int i = 0; i < callbacks.length; i++) {

Callback callback = callbacks[i];

// we only handle NameCallbacks
if (!(callback instanceof NameCallback)) {
throw new UnsupportedCallbackException(callback, "Unrecognized Callback");
}

// send the user name to the name callback:
NameCallback nameCallback = (NameCallback)callback;
nameCallback.setName(userName);
}
}
}





4.Build jar with ant using build.xml
<project name="Expenselink Build" default="all" basedir=".">
<property name="fileDir" value="test" />

<target name="all" depends="build"/>

<target name="build" depends="clean,build.mdf,build.mjf"/>

<target name="clean">
<delete dir="${fileDir}" failonerror="false"/>
<delete file="SimpleSampleIdentityAsserter.jar" failonerror="false"/>
<echo message="Clean finish" />
</target>

<!-- helper to build an MDF (mbean definition file) -->
<target name="build.mdf">
<java dir="${basedir}" fork="false" classname="weblogic.management.commo.WebLogicMBeanMaker">
<arg line="-files ${fileDir}" />
<arg value="-createStubs" />
<arg line="-MDF SimpleSampleIdentityAsserter.xml" />
</java>
<echo message="Created Supporting Classes" />
</target>

<target name="build.mjf">

<copy todir="${fileDir}" flatten="true">
<fileset dir=".">
<include name="*.java" />
</fileset>
</copy>

<java dir="${basedir}" fork="false" classname="weblogic.management.commo.WebLogicMBeanMaker">
<arg line="-MJF SimpleSampleIdentityAsserter.jar" />
<arg line="-files ${fileDir}" />
</java>
<echo message="Created Mbean Jar" />
</target>

</project>



Set WLS environment

[dave@dave src]$ . /app/weblogic121/wlserver_12.1/server/bin/setWLSEnv.sh




[dave@dave src]$ ant
Buildfile: build.xml

clean:
[delete] Deleting directory /home/dave/workspace1211/testIdentityAsserter/src/test
[delete] Deleting: /home/dave/workspace1211/testIdentityAsserter/src/SimpleSampleIdentityAsserter.jar
[echo] Clean finish

build.mdf:
[java] Working directory ignored when same JVM is used.
[java] Parsing the MBean definition file: SimpleSampleIdentityAsserter.xml
[echo] Created Supporting Classes

build.mjf:
[copy] Copying 3 files to /home/dave/workspace1211/testIdentityAsserter/src/test
[java] Working directory ignored when same JVM is used.
[java] Creating an MJF from the contents of directory test...
[java] Compiling the files...
[java] Creating the list.
[java] Doing the compile.
[java] Note: /home/dave/workspace1211/testIdentityAsserter/src/test/SimpleSampleIdentityAsserterProviderImpl.java uses or overrides a deprecated API.
[java] Note: Recompile with -Xlint:deprecation for details.
[java] Note: Some input files use unchecked or unsafe operations.
[java] Note: Recompile with -Xlint:unchecked for details.
[java] WLMaker-SubProcess: : EXTRACT FROM /usr/app/weblogic121/wlserver_12.1/server/lib/mbeantypes/wlManagementMBean.jar
[java] WLMaker-SubProcess: : INTO wlMakerTempDir
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: : Generating the implementations for security MBeans
[java] WLMaker-SubProcess: : Generating for examples.security.providers.identityassertion.simple.SimpleSampleIdentityAsserterMBean to /home/dave/workspace1211/testIdentityAsserter/src/test/examples/security/providers/identityassertion/simple/SimpleSampleIdentityAsserterMBeanImpl.java
[java] WLMaker-SubProcess: : no annotation found for key [i]
[java] WLMaker-SubProcess: : no annotation found for key [velocityCount]
[java] WLMaker-SubProcess: : no annotation found for key [line]
[java] WLMaker-SubProcess: : no annotation found for key [f]
[java] WLMaker-SubProcess: : no annotation found for key [m]
[java] WLMaker-SubProcess: : no annotation found for key [p]
[java] WLMaker-SubProcess: : no annotation found for key [n]
[java] WLMaker-SubProcess: : done
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: : Generating the parsing binders for security MBeans
[java] WLMaker-SubProcess: : Generating for examples.security.providers.identityassertion.simple.SimpleSampleIdentityAsserterMBean to /home/dave/workspace1211/testIdentityAsserter/src/test/examples/security/providers/identityassertion/simple/SimpleSampleIdentityAsserterMBeanBinder.java
[java] WLMaker-SubProcess: : done
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: : Generating the bean infos for security MBeans ...
[java] WLMaker-SubProcess: : Generating for examples.security.providers.identityassertion.simple.SimpleSampleIdentityAsserterMBean to /home/dave/workspace1211/testIdentityAsserter/src/test/examples/security/providers/identityassertion/simple/SimpleSampleIdentityAsserterMBeanImplBeanInfo.java
[java] WLMaker-SubProcess: : no annotation found for key [import]
[java] WLMaker-SubProcess: : no annotation found for key [property]
[java] WLMaker-SubProcess: : no annotation found for key [beanConfigurable]
[java] WLMaker-SubProcess: : no annotation found for key [beanIntfExclude]
[java] WLMaker-SubProcess: : no annotation found for key [propertyMethod]
[java] WLMaker-SubProcess: : no annotation found for key [method]
[java] WLMaker-SubProcess: : Generating Bean Factory Class to test/weblogic/management/security/SIMPLESAMPLEIDENTITYASSERTERBeanInfoFactory.java
[java] WLMaker-SubProcess: : done
[java] WLMaker-SubProcess: : Stopped draining WLMaker-SubProcess:
[java] WLMaker-SubProcess: : Stopped draining WLMaker-SubProcess:
[java] WLMaker-SchemaGen-SubProcess: Generating schema for security provider mbeans ...
[java] WLMaker-SchemaGen-SubProcess: MBEAN TYPES DIR : null
[java] WLMaker-SchemaGen-SubProcess: SET BASE LIB /usr/app/weblogic121/wlserver_12.1/server/lib/schema/weblogic-domain-binding.jar
[java] WLMaker-SchemaGen-SubProcess: Stopped draining WLMaker-SchemaGen-SubProcess
[java] WLMaker-SchemaGen-SubProcess: Stopped draining WLMaker-SchemaGen-SubProcess
[java] Creating the list.
[java] Doing the compile.
[java] Note: Some input files use or override a deprecated API.
[java] Note: Recompile with -Xlint:deprecation for details.
[java] Note: Some input files use unchecked or unsafe operations.
[java] Note: Recompile with -Xlint:unchecked for details.
[java] Creating the MJF...
[java] MJF is created.
[echo] Created Mbean Jar

build:

all:

BUILD SUCCESSFUL



5. copy provider to Weblogic installation dir
see http://docs.oracle.com/cd/E21764_01/web.1111/e13718/ia.htm#autoId18
Note:
WL_HOME\server\lib\mbeantypes is the default directory for installing MBean types.
(Beginning with 9.0, security providers can be loaded from ...\domaindir\lib\mbeantypes as well.)
However, if you want WebLogic Server to look for MBean types in additional directories, use the -Dweblogic.alternateTypesDirectory=<dir> command-line flag when starting your server,
where <dir> is a comma-separated list of directory names. When you use this flag, WebLogic Server will always load MBean types from WL_HOME\server\lib\mbeantypes first,
then will look in the additional directories and load all valid archives present in those directories (regardless of their extension).
For example, if -Dweblogic.alternateTypesDirectory = dirX,dirY, WebLogic Server will first load MBean types from WL_HOME\server\lib\mbeantypes, then any valid archives present in dirX and dirY.


[dave@dave src]$ cp  SimpleSampleIdentityAsserter.jar /app/weblogic121/wlserver_12.1/server/lib/mbeantypes/
[dave@dave src]$


6. configure new provider using WLS console



7. test using Modify HTTP headers plugin or cookie editor ( see previous blog entry)
user token is retrieved by this custom provider from HTTP request header or from cookie

Weblogic logs
SimpleSampleIdentityAsserterProviderImpl.initialize
SimpleSampleIdentityAsserterProviderImpl: getAssertionModuleConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
<Apr 1, 2012 10:44:29 AM CEST> <Notice> <Security> <BEA-090082> <Security initializing using security realm myrealm.>
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
<Apr 1, 2012 10:44:32 AM CEST> <Notice> <LoggingService> <BEA-320400> <The log file /usr/app/domains/base_domain/servers/AdminServer/logs/access.log will be rotated. Reopen the log file if tailing has stopped. This can happen on some platforms, such as Windows.>
<Apr 1, 2012 10:44:32 AM CEST> <Notice> <LoggingService> <BEA-320401> <The log file has been rotated to /usr/app/domains/base_domain/servers/AdminServer/logs/access.log00029. Log messages will continue to be logged in /usr/app/domains/base_domain/servers/AdminServer/logs/access.log.>
<Apr 1, 2012 10:44:34 AM CEST> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to STANDBY.>
<Apr 1, 2012 10:44:34 AM CEST> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to STARTING.>
<Apr 1, 2012 10:44:37 AM CEST> <Notice> <LoggingService> <BEA-320400> <The log file /usr/app/domains/base_domain/servers/AdminServer/logs/base_domain.log will be rotated. Reopen the log file if tailing has stopped. This can happen on some platforms, such as Windows.>
<Apr 1, 2012 10:44:37 AM CEST> <Notice> <LoggingService> <BEA-320401> <The log file has been rotated to /usr/app/domains/base_domain/servers/AdminServer/logs/base_domain.log00032. Log messages will continue to be logged in /usr/app/domains/base_domain/servers/AdminServer/logs/base_domain.log.>
<Apr 1, 2012 10:44:37 AM CEST> <Notice> <Log Management> <BEA-170027> <The server has successfully established a connection with the Domain level Diagnostic Service.>
<Apr 1, 2012 10:44:37 AM CEST> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to ADMIN.>
<Apr 1, 2012 10:44:37 AM CEST> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to RESUMING.>
<Apr 1, 2012 10:44:37 AM CEST> <Notice> <Server> <BEA-002613> <Channel
<Apr 1, 2012 10:44:37 AM CEST> <Notice> <WebLogicServer> <BEA-000331> <Started the WebLogic Server Administration Server "AdminServer" for domain "base_domain" running in development mode.>
<Apr 1, 2012 10:44:37 AM CEST> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to RUNNING.>
<Apr 1, 2012 10:44:37 AM CEST> <Notice> <WebLogicServer> <BEA-000360> <The server started in RUNNING mode.>
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@8393a
userName = dave
SimpleSampleIdentityAsserterProviderImpl: getAssertionModuleConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleLoginModuleImpl.initialize
SimpleSampleLoginModuleImpl.login
userName = dave
userName=dave
groupName = SamplePerimeterAtnUsers
SimpleSampleLoginModule.commit
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@8393a
userName = dave
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@8393a
userName = dave
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@8393a
userName = dave
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@8393a
userName = dave
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@8393a
userName = dave
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@8393a
userName = dave
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@8393a
userName = dave
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleLoginModuleImpl.initialize
SimpleSampleLoginModuleImpl.login
userName = weblogic
userName=weblogic
groupName = SamplePerimeterAtnUsers
SimpleSampleLoginModule.commit
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@8393a
userName = dave
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@8393a
userName = dave
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@8393a
userName = dave
SimpleSampleIdentityAsserterProviderImpl: getAssertionModuleConfiguration
SimpleSampleIdentityAsserterProviderImpl: getConfiguration
SimpleSampleLoginModuleImpl.initialize
SimpleSampleLoginModuleImpl.login
userName = dave
userName=dave
groupName = SamplePerimeterAtnUsers
SimpleSampleLoginModule.commit



Domain config with new provider - default provider must be set to SUFFICIENT because mock users do not exist in WLS LPAP
 <name>base_domain</name>
<domain-version>12.1.1.0</domain-version>
<security-configuration>
<name>base_domain</name>
<realm>
<sec:authentication-provider xsi:type="wls:default-authenticatorType">
<sec:control-flag>SUFFICIENT</sec:control-flag>
</sec:authentication-provider>
<sec:authentication-provider xsi:type="wls:default-identity-asserterType">
<sec:active-type>AuthenticatedUser</sec:active-type>
</sec:authentication-provider>
<sec:authentication-provider xmlns:ext="http://xmlns.oracle.com/weblogic/security/extension" xsi:type="ext:simple-sample-identity-asserterType">
<sec:name>SimpleSampleIdentityAserter</sec:name>
</sec:authentication-provider>
<sec:role-mapper xmlns:xac="http://xmlns.oracle.com/weblogic/security/xacml" xsi:type="xac:xacml-role-mapperType"></sec:role-mapper>
<sec:authorizer xmlns:xac="http://xmlns.oracle.com/weblogic/security/xacml" xsi:type="xac:xacml-authorizerType"></sec:authorizer>
<sec:adjudicator xsi:type="wls:default-adjudicatorType"></sec:adjudicator>
<sec:credential-mapper xsi:type="wls:default-credential-mapperType"></sec:credential-mapper>
<sec:cert-path-provider xsi:type="wls:web-logic-cert-path-providerType"></sec:cert-path-provider>
<sec:cert-path-builder>WebLogicCertPathProvider</sec:cert-path-builder>
<sec:name>myrealm</sec:name>
<sec:password-validator xmlns:pas="http://xmlns.oracle.com/weblogic/security/providers/passwordvalidator" xsi:type="pas:system-password-validatorType">
<sec:name>SystemPasswordValidator</sec:name>
<pas:min-password-length>8</pas:min-password-length>
<pas:min-numeric-or-special-characters>1</pas:min-numeric-or-special-characters>
</sec:password-validator>
</realm>
<default-realm>myrealm</default-realm>




Captured HTTP header using Firefox plugin Live HTTP headers
First run without added token in HTTP request header

http://localhost:7002/samplePerimeterAtnWebApp/SamplePerimeterAtn.jsp

GET /samplePerimeterAtnWebApp/SamplePerimeterAtn.jsp HTTP/1.1
Host: localhost:7001
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:11.0) Gecko/20100101 Firefox/11.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: ADMINCONSOLESESSION=GGfvP4VP5YBZbBcQXb4vQJVF1TRPTq1Qsn4b8yLx1m2H7Q8PlT8v!385259249

HTTP/1.1 401 Unauthorized
Date: Sun, 01 Apr 2012 09:10:09 GMT
Content-Length: 1468
Content-Type: text/html; charset=UTF-8
X-Powered-By: Servlet/3.0 JSP/2.2
----------------------------------------------------------
http://localhost:7001/samplePerimeterAtnWebApp/SamplePerimeterAtn.jsp

GET /samplePerimeterAtnWebApp/SamplePerimeterAtn.jsp HTTP/1.1
Host: localhost:7001
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:11.0) Gecko/20100101 Firefox/11.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: ADMINCONSOLESESSION=GGfvP4VP5YBZbBcQXb4vQJVF1TRPTq1Qsn4b8yLx1m2H7Q8PlT8v!385259249
SamplePerimeterAtnToken: username=dave

HTTP/1.1 200 OK
Date: Sun, 01 Apr 2012 09:10:18 GMT
Content-Length: 90
Content-Type: text/html; charset=ISO-8859-1
Set-Cookie: JSESSIONID=x1yHP4bhj2RjFQL7Ph46hkysTmZC4LXm0gglyL3DsSh2hmTGhVt2!385259249; path=/; HttpOnly
X-Powered-By: Servlet/3.0 JSP/2.2
----------------------------------------------------------

Saturday, March 31, 2012

Weblogic login module test - Firefox plugin to modify HTTP request header (also possible with Opera cookie editor)

Modify Headers Firefox plugin
https://addons.mozilla.org/en-US/firefox/addon/modify-headers/


Modify HTTP header


Capture HTTP headers




Weblogic Identity Assertion Concepts
http://docs.oracle.com/cd/E21764_01/web.1111/e13718/ia.htm#autoId0

Weblogic log of test Identity Asserter
SimpleSampleIdentityAsserterProviderImpl.assertIdentity
Type = SamplePerimeterAtnToken
Token = [B@f99f26
userName = dave



weblogic.xml assigns role to group
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app
xmlns="http://www.bea.com/ns/weblogic/90"
xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.bea.com/ns/weblogic/90 http://www.bea.com/ns/weblogic/90/weblogic-web-app.xsd">

<security-role-assignment>
<role-name>SamplePerimeterAtnRole</role-name>
<principal-name>SamplePerimeterAtnUsers</principal-name>
</security-role-assignment>

</weblogic-web-app>


web.xml configures web resource as secured
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<!-- Specifies the security settings for the SamplePerimeterAtn web app.

This webapp is used to demonstrate how to use identity assertion to
perform perimeter authentication (where someone outside WLS is
responsible for authenticating the user).

Copyright (c) 2005 by BEA Systems, Inc. All Rights Reserved.
-->

<security-constraint>

<!-- all the pages in this webapp are secured -->
<web-resource-collection>
<web-resource-name>SecuredPages</web-resource-name>
<url-pattern>/</url-pattern>
</web-resource-collection>

<!-- only users in the SamplePerimeterAtnRole will
be granted access to the pages in this webapp
-->
<auth-constraint>
<role-name>
SamplePerimeterAtnRole
</role-name>
</auth-constraint>

<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>

</security-constraint>

<!-- Use weblogic.xml to map the SamplePerimeterAtnRole
to the SamplePerimeterAtnUsers group. As a result,
"SamplePerimterAtnUsers" will be granted the role
for this webapp (thus be able to access its pages)
-->
<security-role>
<role-name>
SamplePerimeterAtnRole
</role-name>
</security-role>

<!-- turn on identity assertion

The webapp only specifies that identity assertion should be
used. It does not dictate what kind of tokens to use. Rather,
the client and the identity asserter have to agree on the token
type and format.

- the client is responsible sending in a token that identifies the user

- the identity asserter is responsible for converting that token
to a user name.

- the authenticators are responsible for putting that user
and its groups into the subject

The realm name is not used so set it to "NoSuchRealm". It
has nothing to do with the realm names in the console.

Set the auth method to CLIENT-CERT to turn on identity
assertion for this webapp.
-->
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>NoSuchRealm</realm-name>
</login-config>

</web-app>




Error without modified header

Error 401--Unauthorized
From RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1:
10.4.2 401 Unauthorized

The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.46) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8). If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity MAY include relevant diagnostic information. HTTP access authentication is explained in section 11.



Result page with added header
SamplePerimeterAtn.jsp Subject: Principal: dave Principal: SamplePerimeterAtnUsers Private Credential: dave 


Authorized access with configured Identity Asserter


Modify SimpleSampleIdentityAsserter - set Base64DecodingRequired to false
http://docs.oracle.com/cd/E21764_01/web.1111/e13718/ia.htm#autoId15

 
<MBeanAttribute
Name = "Base64DecodingRequired"
Type = "boolean"
Writeable = "false"
Default = "false"
Description = "See MyIdentityAsserter-doc.xml."
/>


Capture HTTP headers
http://localhost:7001/samplePerimeterAtnWebApp/SamplePerimeterAtn.jsp

GET /samplePerimeterAtnWebApp/SamplePerimeterAtn.jsp HTTP/1.1
Host: localhost:7001
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:11.0) Gecko/20100101 Firefox/11.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: ADMINCONSOLESESSION=0X41P3rh1pbcCGhBn8nJ5yB55R9zds3v6fjD68QMjT5F6cYZqFGJ!-863651884; JSESSIONID=WVJXP3rJnc1tpjTn5SHW4TC5tRLGhgDBgBDTvZqTQGSR67r88XDR!-863651884
SamplePerimeterAtnToken: username=dave

HTTP/1.1 200 OK
Date: Sat, 31 Mar 2012 16:17:46 GMT
Content-Length: 116
Content-Type: text/html; charset=ISO-8859-1
X-Powered-By: Servlet/3.0 JSP/2.2



To change user it is necessary to remove cookie with JSESSIONID

Cookie: JSESSIONID=pZrFP3yQQpFLnJvPLGSTpcgnGRqCQtYJfdfpySLYJG1gd3QCTGWz!-863651884


In Firefox this is done using about:permissions


Opera allows to edit existing cookies
Cookie Information


Cookie Manager



IdentityAsserter MBean in WLS Admin console


All Weblogic users are assigned to group users. This can be used to allow access to authorized application for all authenticated users by mapping role to users principal in web.xml


 weblogic.security.Security.getCurrentSubject()


returns
SamplePerimeterAtn.jsp Subject: Principal: dave Private Credential: dave 



weblogic.xml
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app
xmlns="http://www.bea.com/ns/weblogic/90"
xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.bea.com/ns/weblogic/90 http://www.bea.com/ns/weblogic/90/weblogic-web-app.xsd">

<security-role-assignment>
<role-name>authusers</role-name>
<principal-name>users</principal-name>
</security-role-assignment>

</weblogic-web-app>



web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<!-- Specifies the security settings for the SamplePerimeterAtn web app.

This webapp is used to demonstrate how to use identity assertion to
perform perimeter authentication (where someone outside WLS is
responsible for authenticating the user).

Copyright (c) 2005 by BEA Systems, Inc. All Rights Reserved.
-->

<security-constraint>

<!-- all the pages in this webapp are secured -->
<web-resource-collection>
<web-resource-name>SecuredPages</web-resource-name>
<url-pattern>/</url-pattern>
</web-resource-collection>

<!-- all authenticated users in the authusers will
be granted access to the pages in this webapp
-->
<auth-constraint>
<role-name>authusers</role-name>
</auth-constraint>

<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>

</security-constraint>

<!-- Use weblogic.xml to map the authusers
to the users group. As a result,
"users" will be granted the role
for this webapp (thus be able to access its pages)
-->
<security-role>
<role-name>
authusers
</role-name>
</security-role>

<!-- turn on identity assertion

The webapp only specifies that identity assertion should be
used. It does not dictate what kind of tokens to use. Rather,
the client and the identity asserter have to agree on the token
type and format.

- the client is responsible sending in a token that identifies the user

- the identity asserter is responsible for converting that token
to a user name.

- the authenticators are responsible for putting that user
and its groups into the subject

The realm name is not used so set it to "NoSuchRealm". It
has nothing to do with the realm names in the console.

Set the auth method to CLIENT-CERT to turn on identity
assertion for this webapp.
-->
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>NoSuchRealm</realm-name>
</login-config>

<servlet>
<description></description>
<display-name>AuthenticationSnoop</display-name>
<servlet-name>AuthenticationSnoop</servlet-name>
<servlet-class>dave.AuthenticationSnoop</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AuthenticationSnoop</servlet-name>
<url-pattern>/AuthenticationSnoop</url-pattern>
</servlet-mapping>

</web-app>

Sunday, March 11, 2012

Profile with jvisualvm

http://docs.oracle.com/javase/6/docs/technotes/guides/visualvm/

http://docs.oracle.com/javase/6/docs/technotes/tools/share/jvisualvm.html

Here is how to configure Eclipse IDE launcher
http://visualvm.java.net/eclipse-launcher.html


jvisualvm is part of JDK
[dave@dave ~]$ cd /app/jdk160_29/bin
[dave@dave bin]$ ls
appletviewer java jcontrol jstack policytool unpack200
apt javac jdb jstat rmic wsgen
ControlPanel javadoc jhat jstatd rmid wsimport
extcheck javah jinfo jvisualvm rmiregistry xjc
HtmlConverter javap jmap keytool schemagen
idlj java-rmi.cgi jps native2ascii serialver
jar javaws jrunscript orbd servertool
jarsigner jconsole jsadebugd pack200 tnameserv



Simple program comparing methods to create large string
package dave;

public class StringService {

int STRING_COUNT = 10000;

public String createString(String token) {

String out = "";

for (int i = 0; i < 1000; i++) {
out += token;
}

return out;

}

public String createStringWithStringBuffer(String token) {

StringBuffer out = new StringBuffer();

for (int i = 0; i < STRING_COUNT; i++) {
out.append(token);
}

return out.toString();
}

public String createStringWithStringBuilder(String token) {

StringBuilder out = new StringBuilder();

for (int i = 0; i < STRING_COUNT; i++) {
out.append(token);
}

return out.toString();

}

}




package dave;

public class TestProfiler {

public static void main(String[] args) {

String token = "dave";

StringService stringService = new StringService();

String result = null;

for (int i = 0; i < 10000; i++) {

result = stringService.createString(token);

result = stringService.createStringWithStringBuffer(token);

result = stringService.createStringWithStringBuilder(token);
}

}

}





Profiler with settings to select package


Sampler

Sunday, March 4, 2012

Monitoring with jconsole

Managing WebLogic servers with JConsole
https://blogs.oracle.com/WebLogicServer/entry/managing_weblogic_servers_with

Java Management Extensions (JMX)
http://docs.oracle.com/javase/6/docs/technotes/guides/jmx/index.html

Tutorial: Java Management Extensions (JMX)
http://docs.oracle.com/javase/tutorial/jmx/TOC.html

jconsole usage
Usage: jconsole [ -interval=n ] [ -notile ] [ -pluginpath <path> ] [ -version ] [ connection ... ]

-interval Set the update interval to n seconds (default is 4 seconds)
-notile Do not tile windows initially (for two or more connections)
-pluginpath Specify the path that jconsole uses to look up the plugins
-version Print program version

connection = pid || host:port || JMX URL (service:jmx:<protocol>://...)
pid The process id of a target process
host A remote host name or IP address
port The port number for the remote connection

-J Specify the input arguments to the Java virtual machine
on which jconsole is running



Command line
[dave@dave app]$ jconsole -J-Djava.class.path=$JAVA_HOME/lib/jconsole.jar:$JAVA_HOME/lib/tools.jar:$WL_HOME/server/lib/wlfulclient.jar -J-Djmx.remote.protocol.provider.pkgs=weblogic.management.remote -debug



Plan task FibonacciTask 94
Plan task FibonacciTask 95
Plan task FibonacciTask 96
Plan task FibonacciTask 97
Plan task FibonacciTask 98
Plan task FibonacciTask 99
wm.waitForAll
## [FibWork][ACTIVE] ExecuteThread: '4' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 36
## [FibWork][ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 38
## [FibWork][ACTIVE] ExecuteThread: '11' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 39
## [FibWork][ACTIVE] ExecuteThread: '19' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 40
## [FibWork][ACTIVE] ExecuteThread: '7' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 41
## [FibWork][ACTIVE] ExecuteThread: '15' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 42
## [FibWork][ACTIVE] ExecuteThread: '22' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 43
## [FibWork][ACTIVE] ExecuteThread: '8' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 44
## [FibWork][ACTIVE] ExecuteThread: '4' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 45
## [FibWork][ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 46
## [FibWork][ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 47
## [FibWork][ACTIVE] ExecuteThread: '11' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 48
## [FibWork][ACTIVE] ExecuteThread: '19' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 49
## [FibWork][ACTIVE] ExecuteThread: '7' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 50
## [FibWork][ACTIVE] ExecuteThread: '6' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 51
## [FibWork][ACTIVE] ExecuteThread: '15' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 52



Thread dump
Name: [ACTIVE] ExecuteThread: '11' for queue: 'weblogic.kernel.Default (self-tuning)'
State: RUNNABLE
Total blocked: 98 Total waited: 120

Stack trace:
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.fibonacci(FibonacciTask.java:19)
dave.FibonacciTask.run(FibonacciTask.java:13)
dave.FibWork.run(FibWork.java:29)
weblogic.work.j2ee.J2EEWorkManager$WorkWithListener.run(J2EEWorkManager.java:184)
weblogic.work.ExecuteThread.execute(ExecuteThread.java:256)
weblogic.work.ExecuteThread.run(ExecuteThread.java:221)


Processes
op - 08:37:09 up 10:55, 10 users,  load average: 10.68, 9.80, 5.91
Tasks: 177 total, 1 running, 176 sleeping, 0 stopped, 0 zombie
Cpu(s): 96.1%us, 3.9%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 1533932k total, 1411044k used, 122888k free, 13200k buffers
Swap: 2514100k total, 76348k used, 2437752k free, 369768k cached

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4219 dave 20 0 902m 235m 16m S 171.1 15.7 25:34.98 java
1951 dave 9 -11 160m 4832 4116 S 8.5 0.3 6:55.87 pulseaudio
3824 dave 20 0 530m 208m 24m S 6.2 13.9 6:08.51 firefox
3856 dave 20 0 317m 68m 11m S 6.2 4.6 7:35.17 plugin-containe
4273 dave 20 0 494m 139m 19m S 3.3 9.3 1:03.21 jconsole
1633 root 20 0 137m 22m 14m S 2.6 1.5 9:10.48 Xorg
2402 dave 20 0 124m 13m 9328 S 2.0 0.9 1:02.39 gnome-terminal
1155 root 20 0 0 0 0 S 0.3 0.0 0:34.19 kondemand/0
4337 dave 20 0 1001m 327m 29m S 0.3 21.9 1:14.09 eclipse
4575 dave 20 0 2728 1012 756 R 0.3 0.1 0:00.23 top
1 root 20 0 2888 1032 916 S 0.0 0.1 0:01.66 init
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:01.20 ksoftirqd/0
4 root RT 0 0 0 0 S 0.0 0.0 0:00.90 migration/0
5 root RT 0 0 0 0 S 0.0 0.0 0:00.00 watchdog/0
6 root RT 0 0 0 0 S 0.0 0.0 0:01.11 migration/1
7 root 20 0 0 0 0 S 0.0 0.0 0:00.86 ksoftirqd/1



Stuck thread
<Mar 4, 2012 8:39:35 AM CET> <Error> <WebLogicServer> <BEA-000337> <[STUCK] ExecuteThread: '11' for queue: 'weblogic.kernel.Default (self-tuning)' has been busy for "717" seconds working on the request "Workmanager: daveWM, Version: 1, Scheduled=true, Started=true, Started time: 717593 ms
", which is more than the configured time (StuckThreadMaxTime) of "600" seconds. Stack trace:



JVM overview


Application attributes


Weblogic process threads

Saturday, March 3, 2012

Schedule jobs with WorkManager

Leveraging EJB Timers for J2EE Concurrency
http://www.devx.com/Java/Article/33694/1954

The Work Manager API: Parallel Processing Within a J2EE Container
http://www.devx.com/Java/Article/28815/0


Using Work Managers to Optimize Scheduled Work
http://docs.oracle.com/cd/E14571_01/web.1111/e13701/self_tuned.htm#CNFGD112

Using CommonJ With WebLogic Server
http://docs.oracle.com/cd/E14571_01/web.1111/e13701/self_tuned.htm#i1069944


JBoss WorkManagerTaskExecutor
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.html

Package commonj.work

http://docs.oracle.com/cd/E14571_01/apirefs.1111/e13941/commonj/work/package-summary.html

commonj.work.WorkManager

The WorkManager is the abstraction for dispatching and monitoring asynchronous work and is a factory for creating application short or long lived Works.

WorkManagers are created by the server administrator. The vendor specific systems management console allows the administrator to create one or more WorkManagers and associate a JNDI name with each one. The administrator may specify implementation specific information such as min/max Works for each WorkManager. An application that requires a WorkManager should declare a resource-ref in the EJB or webapp that needs the WorkManager. The vendor descriptor editor or J2EE IDE can be used to bind this resource-ref to a physical WorkManager at deploy or development time. An EJB or servlet can then get a reference to a WorkManager by looking up the resource-ref name in JNDI and then casting it. For example, if the resource-ref was called wm/WorkManager:
 <resource-ref>
<res-ref-name>wm/WorkManager</res-ref-name>
<res-type>commonj.work.WorkManager</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>


The Java code to look this up would look like:
  InitialContext ic = new InitialContext();
WorkManager wm = (WorkManager)ic.lookup("java:comp/env/wm/WorkManager");


The res-auth and res-sharing scopes are ignored in this version of the specification. The EJB or servlet can then use the WorkManager as it needs to.
When a Work is scheduled, the declared context that is present on the thread (the J2EE context) will be saved and propagated to the asynchronous methods that are executed. This J2EE context at minimum will contain the java:comp namespace and ClassLoader of the scheduler unless specified otherwise. Other J2EE contexts such as security or a transactional context may be optionally added by the application server vendor. Global transactions are always available using the java:comp/UserTransaction JNDI name and are used in the same fashion as they are used in servlets and bean-managed transaction Enterprise Java Beans.

A WorkManager can also be a pinned WorkManager. This is a WorkManager obtained using the RemoteWorkItem.getWorkManager method. This allows subsequent scheduleWorks to be send to the same remote WorkManager as the one that is associated with the RemoteWorkItem. Pinned WorkManagers are only supported on vendor implementations that support remote Works. However, applications that follow the programming model will work on all implementations however serializable Works will be executed within the local JVM only on those implementations.

If the scheduled Work is a daemon Work, then the life-cycle of that Work is tied to the application that scheduled it. If the application is stopped, the Work.release() method will be called.


Using the Job Scheduler
http://docs.oracle.com/cd/E14571_01/web.1111/e13733/toc.htm#i1058048

WorkManagerService
package dave;

import java.util.Collection;

import javax.ejb.Remote;

@Remote
public interface WorkManagerService {

public void processJob();

public void processTask(Task task);

public Collection<Task> processTasks(Collection<Task> tasks);

}




CommonJWorkManager
package dave;

import java.util.ArrayList;
import java.util.Collection;

import javax.annotation.Resource;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;

import weblogic.work.ExecuteThread;

import commonj.work.Work;
import commonj.work.WorkEvent;
import commonj.work.WorkException;
import commonj.work.WorkItem;
import commonj.work.WorkManager;

/**
* Session Bean implementation class CommonJWorkManager
*/
@Stateless(mappedName = "WorkManagerService")
@LocalBean
public class CommonJWorkManager implements WorkManagerService {

@Resource(name = "daveWM")
WorkManager wm;

/**
* Default constructor.
*/
public CommonJWorkManager() {
// TODO Auto-generated constructor stub
}

public void processJob() {

try {
System.out.println("## [CommonJWorkManager] executing in: "
+ ((ExecuteThread) Thread.currentThread()).getWorkManager()
.getName());
wm.schedule(new Work() {
public void run() {
ExecuteThread th = (ExecuteThread) Thread.currentThread();
System.out
.println("## [CommonJWorkManager] self-tuning workmanager: "
+ th.getWorkManager().getName());
}

public void release() {
}

public boolean isDaemon() {
return false;
}
});

} catch (WorkException e) {
e.printStackTrace();
}

}

public void processTask(Task task) {

System.out.println("Plan task " + task.getName());

try {
System.out.println("## [CommonJWorkManager] executing in: "
+ ((ExecuteThread) Thread.currentThread()).getWorkManager()
.getName());

wm.schedule(new FibWork(task));

} catch (WorkException e) {
e.printStackTrace();
}

}

@Override
public Collection<Task> processTasks(Collection<Task> tasks) {

Collection<WorkItem> workItems = new ArrayList<WorkItem>();
Collection<Task> out = new ArrayList<Task>();

try {
System.out.println("## [CommonJWorkManager] executing in: "
+ ((ExecuteThread) Thread.currentThread()).getWorkManager()
.getName());

for (Task task : tasks) {
System.out.println("Plan task " + task.getName());
WorkItem workItem = wm.schedule(new FibWork(task));
workItems.add(workItem);
}
System.out.println("wm.waitForAll");
wm.waitForAll(workItems, 60000);
for (WorkItem workItem : workItems) {
System.out.println("workItem.result=" + workItem.getStatus());
if (workItem.getStatus() == WorkEvent.WORK_COMPLETED
|| workItem.getStatus() == WorkEvent.WORK_REJECTED) {
FibWork work = (FibWork) workItem.getResult();
if (work != null) {
out.add(work.getTask());
System.out.println("workItem task"
+ work.getTask().getName() + " result="
+ ((FibonacciTask) work.getTask()).getResult());
}
}
}

} catch (WorkException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}

return out;

}

}



FibWork
package dave;

import weblogic.work.ExecuteThread;
import commonj.work.Work;

public class FibWork implements Work {

Task task;

public Task getTask() {
return task;
}

public void setTask(Task task) {
this.task = task;
}

public FibWork(Task task) {
this.task = task;
}

@Override
public void run() {

ExecuteThread th = (ExecuteThread) Thread.currentThread();
System.out.println("## [FibWork] self-tuning workmanager: "
+ th.getWorkManager().getName());
System.out.println("Running task " + task.getName());
task.run();

}

@Override
public boolean isDaemon() {
return false;
}

@Override
public void release() {
// TODO Auto-generated method stub

}

}



FibonacciCalculatorBean
package dave;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Stateless;

/**
* Session Bean implementation class FibonacciCalculatorBean
*/
@Stateless(mappedName = "FibonacciCalculator")
public class FibonacciCalculatorBean implements FibonacciCalculator {

@EJB(mappedName = "WorkManagerService")
private WorkManagerService workManagerService;


public void calculateFibonacci(int[] numbers) {
long initialTime = System.currentTimeMillis();
List<Task> tasks = new ArrayList<Task>();
for (int i = 1; i < numbers.length; i++){
FibonacciTask fibonacciTask = new FibonacciTask(numbers[i]);
tasks.add(fibonacciTask);
}
Collection<Task> resultTasks = workManagerService.processTasks(tasks);
long elapsedTime = System.currentTimeMillis() - initialTime;
printFibonacciResults(resultTasks, elapsedTime);
}

private void printFibonacciResults(Collection<Task> tasks, long elapsedTime) {
System.out.println("** Completed Fibonacci Computations in " + elapsedTime
+ "ms **");
for (Task task : tasks) {
FibonacciTask ft = (FibonacciTask) task;
System.out.println("Fibonacci(" + ft.getN() + ") = " + ft.getResult());
}
}

}



weblogic-ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-ejb-jar xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-ejb-jar" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd http://xmlns.oracle.com/weblogic/weblogic-ejb-jar http://xmlns.oracle.com/weblogic/weblogic-ejb-jar/1.3/weblogic-ejb-jar.xsd">
<!--weblogic-version:12.1.1-->
<wls:weblogic-enterprise-bean>
<wls:ejb-name>CommonJWorkManager</wls:ejb-name>
<wls:stateless-session-descriptor></wls:stateless-session-descriptor>
<wls:transaction-descriptor>
<wls:trans-timeout-seconds>300</wls:trans-timeout-seconds>
</wls:transaction-descriptor>
<wls:dispatch-policy>daveWM</wls:dispatch-policy>
</wls:weblogic-enterprise-bean>

<wls:work-manager>
<wls:name>daveWM</wls:name>

<wls:max-threads-constraint>
<wls:name>MaxThreadsCountTen</wls:name>
<wls:count>10</wls:count>
</wls:max-threads-constraint>
</wls:work-manager>
</wls:weblogic-ejb-jar>


Client
package dave;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.InitialContext;

public class FibonacciClient {

public static void main(String[] args)

{
try

{
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "weblogic");
env.put(Context.SECURITY_CREDENTIALS, "weblogic123");
env.put(Context.PROVIDER_URL, "t3://localhost:7001");
Context ctx = new InitialContext(env);
System.out.println("Initial Context created");

/*TimerSession timerSession = (TimerSession) ctx
.lookup("TimerSession#dave.TimerSession");
timerSession.createTimer(100000);*/

FibonacciCalculator fibonacciCalculator = (FibonacciCalculator) ctx
.lookup("FibonacciCalculator#dave.FibonacciCalculator");
System.out.println("lookup successful");
System.out.println("Calling EJB method . . .");
int fibMax = 10;
int[] numbers =new int[fibMax];
for(int i = 1; i< fibMax; i++){
numbers[i] = i;
}
fibonacciCalculator.calculateFibonacci(numbers);
}

catch (Exception e) {
e.printStackTrace();
}
}
}



Output
## [CommonJWorkManager] executing in: default
Plan task FibonacciTask 1
Plan task FibonacciTask 2
Plan task FibonacciTask 3
## [FibWork] self-tuning workmanager: daveWM
Running task FibonacciTask 1
Plan task FibonacciTask 4
## [FibWork] self-tuning workmanager: daveWM
## [FibWork] self-tuning workmanager: daveWM
Running task FibonacciTask 2
Running task FibonacciTask 3
## [FibWork] self-tuning workmanager: daveWM
Running task FibonacciTask 4
Plan task FibonacciTask 5
Plan task FibonacciTask 6
## [FibWork] self-tuning workmanager: daveWM
Running task FibonacciTask 5
## [FibWork] self-tuning workmanager: daveWM
Running task FibonacciTask 6
Plan task FibonacciTask 7
Plan task FibonacciTask 8
## [FibWork] self-tuning workmanager: daveWM
Plan task FibonacciTask 9
## [FibWork] self-tuning workmanager: daveWM
Running task FibonacciTask 8
## [FibWork] self-tuning workmanager: daveWM
Running task FibonacciTask 9
Running task FibonacciTask 7
wm.waitForAll
workItem.result=4
workItem taskFibonacciTask 1 result=1
workItem.result=4
workItem taskFibonacciTask 2 result=1
workItem.result=4
workItem taskFibonacciTask 3 result=2
workItem.result=4
workItem taskFibonacciTask 4 result=3
workItem.result=4
workItem taskFibonacciTask 5 result=5
workItem.result=4
workItem taskFibonacciTask 6 result=8
workItem.result=4
workItem taskFibonacciTask 7 result=13
workItem.result=4
workItem taskFibonacciTask 8 result=21
workItem.result=4
workItem taskFibonacciTask 9 result=34
** Completed Fibonacci Computations in 17ms **
Fibonacci(1) = 1
Fibonacci(2) = 1
Fibonacci(3) = 2
Fibonacci(4) = 3
Fibonacci(5) = 5
Fibonacci(6) = 8
Fibonacci(7) = 13
Fibonacci(8) = 21
Fibonacci(9) = 34



WorkItems not finished on timeout - result 3
workItem.result=4
workItem taskFibonacciTask 41 result=165580141
workItem.result=4
workItem taskFibonacciTask 42 result=267914296
workItem.result=4
workItem taskFibonacciTask 43 result=433494437
workItem.result=3
workItem.result=3
workItem.result=3
workItem.result=3
workItem.result=3
workItem.result=3
** Completed Fibonacci Computations in 60115ms **
Fibonacci(1) = 1
Fibonacci(2) = 1
Fibonacci(3) = 2
Fibonacci(4) = 3
Fibonacci(5) = 5
Fibonacci(6) = 8
Fibonacci(7) = 13
Fibonacci(8) = 21
Fibonacci(9) = 34
Fibonacci(10) = 55
Fibonacci(11) = 89
Fibonacci(12) = 144
Fibonacci(13) = 233






Requests waiting for processing


All threads


Threads used
## [CommonJWorkManager] executing in: default
Plan task FibonacciTask 1
Plan task FibonacciTask 2
## [FibWork][ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 1
Plan task FibonacciTask 3
Plan task FibonacciTask 4
## [FibWork][ACTIVE] ExecuteThread: '15' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 3
## [FibWork][ACTIVE] ExecuteThread: '31' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 2
Plan task FibonacciTask 5
## [FibWork][ACTIVE] ExecuteThread: '20' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 4
## [FibWork][ACTIVE] ExecuteThread: '29' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 5
Plan task FibonacciTask 6
Plan task FibonacciTask 7
## [FibWork][ACTIVE] ExecuteThread: '6' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 6
## [FibWork][ACTIVE] ExecuteThread: '30' for queue: 'weblogic.kernel.Default (self-tuning)' self-tuning workmanager: daveWM
Running task FibonacciTask 7
Plan task FibonacciTask 8
Plan task FibonacciTask 9