To create a custom database authenticator for oracle weblogic server, you will have to implement the AuthenticationProviderV2, create an MBean definition file and create a class that implements LoginModule.
I am providing the sample code below and also the steps to create and install it on your server.
DBAuthenticationProviderImpl
package com.wonders.security.dbauthentication; import java.util.HashMap; 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.spi.AuthenticationProviderV2; import weblogic.security.spi.IdentityAsserterV2; import weblogic.security.spi.PrincipalValidator; import weblogic.security.spi.SecurityServices; import weblogic.security.principal.WLSGroupImpl; import weblogic.security.principal.WLSUserImpl; public class DBAuthenticationProviderImpl implements AuthenticationProviderV2 { private String a_sDescription; private DBAuthenticatorDatabase a_oDatabase; private LoginModuleControlFlag a_oControlFlag; private String a_sDataSourceJNDIName; private String a_sSystemUserName; public void initialize(ProviderMBean mbean, SecurityServices services) { System.out.println("DBAuthenticationProviderImpl.initialize"); DBAuthenticatorMBean oMyMBean = (DBAuthenticatorMBean)mbean; a_sDescription = oMyMBean.getDescription() + "n" + oMyMBean.getVersion(); a_sDataSourceJNDIName = oMyMBean.getDataSourceJNDIName(); a_sSystemUserName = oMyMBean.getSystemUserName(); a_oDatabase = new DBAuthenticatorDatabase(a_sDataSourceJNDIName, a_sSystemUserName); String flag = oMyMBean.getControlFlag(); if (flag.equalsIgnoreCase("REQUIRED")) { a_oControlFlag = LoginModuleControlFlag.REQUIRED; } else if (flag.equalsIgnoreCase("OPTIONAL")) { a_oControlFlag = LoginModuleControlFlag.OPTIONAL; } else if (flag.equalsIgnoreCase("REQUISITE")) { a_oControlFlag = LoginModuleControlFlag.REQUISITE; } else if (flag.equalsIgnoreCase("SUFFICIENT")) { a_oControlFlag = LoginModuleControlFlag.SUFFICIENT; } else { throw new IllegalArgumentException("invalid flag value" + flag); } } public String getDescription() { return a_sDescription; } public void shutdown() { System.out.println("DBAuthenticatorDatabase.shutdown"); } private AppConfigurationEntry getConfiguration(HashMap options) { options.put("database", a_oDatabase); return new AppConfigurationEntry( "com.wonders.security.dbauthentication.DBLoginModuleImpl", a_oControlFlag, options ); } public AppConfigurationEntry getLoginModuleConfiguration() { HashMap oOptions = new HashMap(); oOptions.put("DataSourceJNDIName",a_sDataSourceJNDIName); oOptions.put("SystemUserName",a_sSystemUserName); return getConfiguration(oOptions); } public AppConfigurationEntry getAssertionModuleConfiguration() { HashMap options = new HashMap(); options.put("IdentityAssertion","true"); return getConfiguration(options); } public PrincipalValidator getPrincipalValidator() { return new PrincipalValidatorImpl(); } public IdentityAsserterV2 getIdentityAsserter() { return null; } }
DBLoginModuleImpl
package com.wonders.security.dbauthentication; 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.spi.WLSGroup; import weblogic.security.spi.WLSUser; import weblogic.security.principal.WLSGroupImpl; import weblogic.security.principal.WLSUserImpl; public class DBLoginModuleImpl implements LoginModule { private Subject a_oSubject; private CallbackHandler a_oCallbackHandler; private DBAuthenticatorDatabase a_oDatabase; // Determine whether this is a login or assert identity private boolean a_bIsIdentityAssertion; // Authentication status private boolean a_bLoginSucceeded; private boolean a_bPrincipalsInSubject; private Vector a_collPrincipalsForSubject = new Vector(); public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { // only called (once!) after the constructor and before login System.out.println("DBLoginModuleImpl.initialize"); this.a_oSubject = subject; this.a_oCallbackHandler = callbackHandler; // Check for Identity Assertion option a_bIsIdentityAssertion = "true".equalsIgnoreCase((String)options.get("IdentityAssertion")); a_oDatabase = (DBAuthenticatorDatabase)options.get("database"); } public boolean login() throws LoginException { // only called (once!) after initialize System.out.println("DBLoginModuleImpl.login"); // loginSucceeded should be false // principalsInSubject should be false Callback[] callbacks = getCallbacks(); String sUserName = getUserName(callbacks); if (sUserName.length() > 0) { if (!a_oDatabase.userExists_modified(sUserName)) { throwFailedLoginException("Authentication Failed: User " + sUserName + " doesn't exist."); } if (!a_bIsIdentityAssertion) { String passwordWant = null; try { String sPasswordHave = getPasswordHave( sUserName, callbacks ); if ( !a_oDatabase.authenticate_modified( sUserName, sPasswordHave, false ) ) { throwFailedLoginException( "Authentication Failed: User " + sUserName + " bad password. "); } } catch (NotFoundException shouldNotHappen) { System.out.println(shouldNotHappen.getCause()); System.out.println(shouldNotHappen.getStackTrace()); } } } a_bLoginSucceeded = true; a_collPrincipalsForSubject.add(new WLSUserImpl(sUserName)); addGroupsForSubject(sUserName); return a_bLoginSucceeded; } 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("DBLoginModuleImpl.commit"); if (a_bLoginSucceeded) { a_oSubject.getPrincipals().addAll(a_collPrincipalsForSubject); a_bPrincipalsInSubject = true; return true; } else { return false; } } public boolean abort() throws LoginException { // The abort method is called to abort the authentication process. This is // phase 2 of authentication when phase 1 fails. It is called if the // LoginContext's overall authentication failed. // loginSucceeded should be true or false // user should be null if !loginSucceeded, otherwise null or not-null // group should be null if user == null, otherwise null or not-null // principalsInSubject should be false if user is null, otherwise true // or false System.out.println("DBLoginModuleImpl.abort"+a_bPrincipalsInSubject); if (a_bPrincipalsInSubject) { a_oSubject.getPrincipals().removeAll(a_collPrincipalsForSubject); a_bPrincipalsInSubject = false; } return true; } public boolean logout() throws LoginException { // should never be called System.out.println("DBLoginModuleImpl.logout"); return true; } private void throwLoginException(String msg) throws LoginException { System.out.println("Throwing LoginException(" + msg + ")"); throw new LoginException(msg); } private void throwFailedLoginException(String msg) throws FailedLoginException { System.out.println("Throwing FailedLoginException(" + msg + ")"); throw new FailedLoginException(msg); } private Callback[] getCallbacks() throws LoginException { if (a_oCallbackHandler == null) { throwLoginException("No CallbackHandler Specified"); } if (a_oDatabase == null) { throwLoginException("database not specified"); } Callback[] callbacks; //modified by faisal a_bIsIdentityAssertion=false; if (a_bIsIdentityAssertion) { callbacks = new Callback[1]; } else { callbacks = new Callback[2]; callbacks[1] = new PasswordCallback("password: ",false); } callbacks[0] = new NameCallback("username: "); try { a_oCallbackHandler.handle(callbacks); } catch (IOException e) { throw new LoginException(e.toString()); } catch (UnsupportedCallbackException e) { throwLoginException(e.toString() + " " + e.getCallback().toString()); } return callbacks; } private String getUserName(Callback[] callbacks) throws LoginException { String userName = ((NameCallback)callbacks[0]).getName(); if (userName == null) { throwLoginException("Username not supplied."); } System.out.println("tuserNamet= " + userName); return userName; } private void addGroupsForSubject(String userName) { for (Enumeration e = a_oDatabase.getUserGroups_modified(userName); e.hasMoreElements();) { String groupName = (String)e.nextElement(); System.out.println("tgroupNamet= " + groupName); a_collPrincipalsForSubject.add(new WLSGroupImpl(groupName)); } } 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("tpasswordHavet= " + passwd); return passwd; } }
DBAuthenticatorDatabase
package com.wonders.security.dbauthentication; import java.sql.*; import java.util.Enumeration; import java.util.Vector; import java.util.*; import javax.naming.*; import javax.sql.DataSource; import weblogic.logging.NonCatalogLogger; import weblogic.management.utils.NotFoundException; import java.security.MessageDigest; import com.wonders.security.*; public final class DBAuthenticatorDatabase { String a_sDataSourceName; String a_sSystemUserName; protected static DataSource a_oDataSource = null; private static NonCatalogLogger a_oLogger = new NonCatalogLogger( "Security.Authentication" ); //Constructor public DBAuthenticatorDatabase(String p_sDataSourceName, String p_sSystemUserName ) { a_oLogger.debug( "DBAuthenticatorDatabase.constructor()"); this.a_sDataSourceName = p_sDataSourceName; this.a_sSystemUserName = p_sSystemUserName; } public void getDataSource() throws Exception { a_oLogger.debug("Dataset name is = " + a_sDataSourceName); // If we don't already have a reference to the data source, if ( a_oDataSource == null ) {try { // Get the initial context from the server and use it // to retrieve the data source Context oCtx = new InitialContext(); a_oDataSource = ( javax.sql.DataSource ) oCtx.lookup( a_sDataSourceName ); } catch ( NamingException e ) { a_oLogger.error( "Failed to get initial context or DataSource: " + e, e ); throw e; } } } // Return a JDBC connection from the pool protected Connection getConnection() throws Exception { java.sql.Connection oNewConn = null; try { if (a_oDataSource == null) { getDataSource(); } oNewConn = a_oDataSource.getConnection(); } catch ( Exception e ) { a_oLogger.error( "Failed to get connection: " + e, e ); throw new Exception(); } return oNewConn; } //Return an existing connection to the DB pool protected void returnConnection( Connection p_oCon ) { try { if ( p_oCon != null ) { p_oCon.close(); p_oCon = null; } } catch ( SQLException e ) { a_oLogger.error( "Error while returning connection to the pool: " + e, e ); } } public synchronized boolean authenticate_modified( String p_sUser, String p_sPassword, boolean p_bEncrypted ) throws NotFoundException { try { // Get a database connection from the connection pool Connection oCon = getConnection(); PreparedStatement oPs = oCon.prepareStatement("SELECT U_PASSWORD FROM USERS WHERE U_NAME=?"); oPs.setString(1, p_sUser); ResultSet oRs = oPs.executeQuery(); boolean pExists = oRs.next(); String password=""; if(pExists) password = oRs.getString("U_PASSWORD"); System.out.println("password=="+password+"p_sPassword=="+p_sPassword); if(password.equals(p_sPassword)) return true; }catch ( SQLException e ) { e.printStackTrace(); } catch ( Exception ex ) { ex.printStackTrace(); } return false; } public synchronized Enumeration getUserGroups_modified( String p_sUser ){ List colRoleList = new ArrayList(); System.out.println( "DBAuthenticatorDatabase.getUserGroups_modified() for: " + p_sUser); try { // Get a database connection from the connection pool Connection oCon = getConnection(); PreparedStatement oPs = oCon.prepareStatement("SELECT G_NAME FROM GROUPMEMBERS WHERE G_MEMBER=?"); oPs.setString(1, p_sUser); System.out.println( "DBAuthenticatorDatabase.getUserGroups_modified():executing query "); ResultSet oRs = oPs.executeQuery(); System.out.println( "DBAuthenticatorDatabase.getUserGroups_modified(): query executed"); while (oRs.next()) colRoleList.add(oRs.getString(1)); returnConnection( oCon ); } catch ( SQLException e ) { a_oLogger.error( "Error checking for user existence: " + e, e ); } catch ( Exception ex ) { a_oLogger.error( "Error checking for user existance: " + ex, ex ); } return Collections.enumeration(colRoleList); } public synchronized boolean userExists_modified( String p_sUser ) { a_oLogger.debug( "DBAuthenticatorDatabase.userExists_modified() for: " + p_sUser); System.out.println( "DBAuthenticatorDatabase.userExists_modified() for: " + p_sUser); Connection oCon = null; boolean bExists = false; //If we are trying to log in as system, then stop further attempts, which will require JNDI access in this facility //Authentication instead will pass to the default authenticator, where system user will be defined /* if (p_sUser.equals(a_sSystemUserName)) return false; */ try { // Get a database connection from the connection pool oCon = getConnection(); // Prepare the neccessary statements in order to check if the user exists PreparedStatement oUsersStmt = oCon.prepareStatement("Select U_NAME,U_PASSWORD From USERS where U_NAME=?"); // Query for a user by the specified name oUsersStmt.setString( 1, p_sUser ); System.out.println("executing query!"); ResultSet oRS = oUsersStmt.executeQuery(); System.out.println(" query executed"); // Store the result of the record set (whether we found users or not) bExists = oRS.next(); if(bExists = true) System.out.println("usename from database "+oRS.getString("U_NAME")+" bExists"+bExists); else{ System.out.println("User Doesnt Exist!!"); } // Clean everything up oRS.close(); oUsersStmt.close(); } catch ( SQLException e ) { a_oLogger.error( "Error checking for user existance: " + e, e ); } catch ( Exception ex ) { a_oLogger.error( "Error checking for user existance: " + ex, ex ); } finally { // Return the database connection to the pool returnConnection( oCon ); } // Return the saved result a_oLogger.debug( "DBAuthenticatorDatabase.userExists_modified() for: " + p_sUser + " = " + bExists); return bExists; } } // class DBAuthenticatorDatabase
DBAuthenticator.xml
<?xml version="1.0" ?> <!DOCTYPE MBeanType SYSTEM "commo.dtd"> <MBeanType Name = "DBAuthenticator" DisplayName = "DBAuthenticator" Package = "com.wonders.security.dbauthentication" Extends = "weblogic.management.security.authentication.Authenticator" PersistPolicy = "OnUpdate" > <MBeanAttribute Name = "ProviderClassName" Type = "java.lang.String" Writeable = "false" Default = ""com.wonders.security.dbauthentication.DBAuthenticationProviderImpl"" /> <MBeanAttribute Name = "Description" Type = "java.lang.String" Writeable = "false" Default = "" Securities Security Provider - DB Authentication "" /> <MBeanAttribute Name = "Version" Type = "java.lang.String" Writeable = "false" Default = ""1.0"" /> <MBeanAttribute Name = "DataSourceJNDIName" Type = "java.lang.String" Writeable = "true" Default = ""pointbaseds"" /> <MBeanAttribute Name = "SystemUserName" Type = "java.lang.String" Writeable = "true" Default = ""system"" /> </MBeanType>
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="DBAuthenticator.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 DBAuthenticator.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 DBAuthenticator.jar" /> <arg line="-files ${fileDir}" /> </java> <echo message="Created Mbean Jar" /> </target> </project>
Keep all the files in one location. Copy commo.dtd from WL_HOMEserverlib to this folder. Also copy WL_HOMEserverbinsetWLSEnv.cmd to this folder. The folder will look like this.
Run ant to build the Mbean Jar file.
C:ReplicationsDatabaseAuthenticator>ant build
Buildfile: build.xml
clean:
[delete] Deleting directory C:ReplicationsDatabaseAuthenticatortest
[echo] Clean finish
build.mdf:
[java] Working directory ignored when same JVM is used.
[java] Parsing the MBean definition file: DBAuthenticator.xml
[echo] Created Supporting Classes
build.mjf:
[copy] Copying 3 files to C:ReplicationsDatabaseAuthenticatortest
[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: C:ReplicationsDatabaseAuthenticatortestDBAuthenticationPro
viderImpl.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 C:/bea103/WLSERV~1.3/serverlibm
beantypeswlManagementMBean.jar
[java] WLMaker-SubProcess: : INTO wlMakerTempDir
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: :
[java] WLMaker-SubProcess: : Generating the implementations for security MB
eans
[java] WLMaker-SubProcess: : Generating for com.wonders.security.dbauthenti
cation.DBAuthenticatorMBean to C:ReplicationsDatabaseAuthenticatortestcomwo
nderssecuritydbauthenticationDBAuthenticatorMBeanImpl.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 MB
eans
[java] WLMaker-SubProcess: : Generating for com.wonders.security.dbauthenti
cation.DBAuthenticatorMBean to C:ReplicationsDatabaseAuthenticatortestcomwo
nderssecuritydbauthenticationDBAuthenticatorMBeanBinder.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 com.wonders.security.dbauthenti
cation.DBAuthenticatorMBean to C:ReplicationsDatabaseAuthenticatortestcomwo
nderssecuritydbauthenticationDBAuthenticatorMBeanImplBeanInfo.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 testweblogic
managementsecurityDBAUTHENTICATORBeanInfoFactory.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 provide
r mbeans …
[java] WLMaker-SchemaGen-SubProcess: MBEAN TYPES DIR : null
[java] WLMaker-SchemaGen-SubProcess: SET BASE LIB C:bea103WLSERV~1.3serv
erlibschemaweblogic-domain-binding.jar
[java] WLMaker-SchemaGen-SubProcess: Stopped draining WLMaker-SchemaGen-Sub
Process
[java] WLMaker-SchemaGen-SubProcess: Stopped draining WLMaker-SchemaGen-Sub
Process
[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:
BUILD SUCCESSFUL
Total time: 43 seconds
Copy DBAuthenticator.jar to WL_HOMEserverlibmbeantypes folder.
Start your server and configure your custom database authenticator.
You will also need to create a datasource
The database should have the following schema for this example.
CREATE TABLE USERS ( U_NAME VARCHAR(200) NOT NULL, U_PASSWORD VARCHAR(50) NOT NULL, U_DESCRIPTION VARCHAR(1000)); ALTER TABLE USERS ADD CONSTRAINT PK_USERS PRIMARY KEY (U_NAME); CREATE TABLE GROUPS ( G_NAME VARCHAR(200) NOT NULL, G_DESCRIPTION VARCHAR(1000) NULL); ALTER TABLE GROUPS ADD CONSTRAINT PK_GROUPS PRIMARY KEY (G_NAME); CREATE TABLE GROUPMEMBERS ( G_NAME VARCHAR(200) NOT NULL, G_MEMBER VARCHAR(200) NOT NULL); ALTER TABLE GROUPMEMBERS ADD CONSTRAINT PK_GROUPMEMS PRIMARY KEY ( G_NAME, G_MEMBER ); ALTER TABLE GROUPMEMBERS ADD CONSTRAINT FK1_GROUPMEMBERS FOREIGN KEY ( G_NAME ) REFERENCES GROUPS (G_NAME) ON DELETE CASCADE insert into USERS (U_NAME,U_PASSWORD,U_DESCRIPTION) values('system','weblogic','admin user'); insert into GROUPS (G_NAME,G_DESCRIPTION) values('Adminsitrators','Adminsitrators'); insert into GROUPMEMBERS (G_NAME,G_MEMBER) values('Administrators','system');
Once your provide is created, change the control flag of the default authenticator to SUFFICIENT.
This should be the order of the authentication providers.
You can create more users in the database
insert into USERS (U_NAME,U_PASSWORD,U_DESCRIPTION) values(‘faisal’,’faisal’,’admin user’);
insert into GROUPS (G_NAME,G_DESCRIPTION) values(‘Administrators’,’Administrators’);
insert into GROUPMEMBERS (G_NAME,G_MEMBER) values(‘Administrators’,’faisal’);
Restart your server.
Try to log in to your secure application with the new user id.
You should see the following debug for a successful login.
DBLoginModuleImpl.initialize
DBLoginModuleImpl.login
userName = faisal
DBAuthenticatorDatabase.userExists_modified() for: faisal
executing query!
query executed
usename from database faisal bExiststrue
passwordHave = faisal
password==faisalp_sPassword==faisal
DBAuthenticatorDatabase.getUserGroups_modified() for: faisal
DBAuthenticatorDatabase.getUserGroups_modified():executing query
DBAuthenticatorDatabase.getUserGroups_modified(): query executed
groupName = Administrators
DBLoginModuleImpl.commit
Cheers!
Wonders Team
Thank you for sample!
Why is there: “Also copy WL_HOME\server\bin\setWLSEnv.cmd to this folder.”, if the script should be rather called then copied?
“Also copy WL_HOME\server\bin\setWLSEnv.cmd to this folder.” You do not need to copy it, but run script in shell used by ant.