In this post we are going to put WebLogic 12c to work. We start with setting-up our playground by installing VMWare WorkStation and creating a RedHat Enterprise Linux virtual machine. We continue by setting-up a basic WebLogic domain. Next, we set-up a cluster. Here, we show by using a WLST script how to set-up service migration for JTA and JMS. Once the clustered environment is set-up, we build an application that uses JavaEE6 features in order to test the environment.

Set-up our playground

To create a virtual machine we are going to use VMWare WorkStation (VMware-workstation-5.5.6-80404.exe).

  • Open VMWare WorkStation and click new, virtual machine.
  • Click next on the welcome screen.
  • Choose custom and click next.
  • Choose a certain virtual machine format and click next.
  • Choose a guest operating system. In this example we are going to use RedHat Enterprise Linux. Click next.
  • Enter the following parameters:
    • virtual machine name: RedHat Enterprise Linux 5
    • location: c:\temp\vmredhat\
  • Click next.
  • Choose the number of processors and click next.
  • Configure the amount of memory for the virtual machine and click next.
  • Select the network type and click next.
  • Select the I/O adapter type and click next.
  • Select create a new virtual disk and click next.
  • Enter the disk capacity (select allocate disk space now and split disk into 2GB files) and click next. One important thing to note is that the disk capacity cannot be changed, this in contrast to the number of processors and the amount of memory that can be adjusted at will once the virtual machine is created.
  • Enter the name for the disk file.
  • Click finish

Next RedHat Enterprise Linux must be installed in the virtual machine. When an iso file is used that is located on disk, we have to edit the virtual machine:

  • In VMWare WorkStation click edit virtual machine settings.
  • Click on CD-ROM and select use iso image and browse to the appropriate location.

Start the virtual machine and on first start-up the RedHat Enterprise Linux installer will run:

  • Press enter to start the installation (note that various install options are available, for example linux mem=2048m noprobe, see the function buttons for more details).
  • The CD media test screen is shown, run the test to be sure the iso image (rhel-server-5.7-x86_64-dvd.iso) is correct. Note that, usually when a 64-bit environment is to be virtualized the VT option of the (in this case Intel) processor must be enabled.
  • After a few moments the welcome screen is shown, click next.
  • Choose the installation language and click next.
  • Choose the keyboard type to be used and click next.
  • Enter the installation number (can also be skipped) when applicable and click next.
  • The hard drive partitioning screen is shown. Usually the default is suitable for most cases:
    • select – remove linux partitions on selected drives and create default layout.
    • to review the layout, select review and modify partition layout.
  • Click next (a warning is shown, click yes).
  • The partition layout can be modified at will, which we are not going to do, click next
  • Accept the defaults on the boot loader screen and click next.
  • Edit the network devices when necessary and click next.
  • Choose the location (for time settings) and click next
  • Enter the password for the superuser (root) and click next.
  • The option to customize the packages to be installed is offered, click next.
  • When the customize option was chosen we can choose which packages to install (the defaults are in general fine), click next.
  • Click next to start the installation and be patient for a while.
  • Click reboot (the installation is finished).

On first start up, RedHat Enterprise Linux offers the system to be configured:

  • Click forward on the welcome screen.
  • The license agreement is shown, click yes and click forward.
  • The option to configure the firewall is offered (disable), click forward.
  • Security Enhanced Linux (enforcing), click forward.
  • Disable kdump, click forward.
  • Set the date and time and click forward.
  • Configure software updates and click forward.
  • Create a user:
    • username: oracle
    • fullname: oracle
    • password: magic11g
    • confirm password: magic11g
  • Click forward.
  • Configure a sound card, click forward.
  • Install additional Cd’s when needed, click finish.

To get some performance enhancement install VMWare tools:

  • When the virtual machine is running, in VMWare WorkStation, click VM and choose install VMWare tools.
  • To install VMWare tools by hand, obtain a distribution, for example, VMwareTools-4.0.0-208167.tar.gz and follow these steps:
    • su root
    • tar xzf VMwareTools-4.0.0-208167.tar.gz
    • cd vmware-tools-distrib
    • ./vmware-install.pl
    • follow the instructions presented on the screen (which means pressing enter a lot).
  • Log out and log in again – now we are able to adjust, for example, by clicking system, preferences, the screen resolution.

Now that we have our playground ready we continue with installing WebLogic 12c.

Set-up a basic WebLogic environment

First, we choose an installation directory, for example /home/oracle/weblogic12.1.1. This will be our middleware home in the installation of WebLogic.

To install JRockit we follow these steps:

  • Run the file jrrt-4.0.1-1.6.0-linux-x64.bin (JRockit can be downloaded here).
  • Click next on the welcome screen.
  • Define the install directory /home/oracle/jrrt-4.0.1-1.6.0 and click next.
  • Optionally select extra components and click next.
  • When the installation is finished click done.

To WebLogic we follow these steps:

  • Navigate to the JDK’s bin directory (/home/oracle/jrrt-4.0.1-1.6.0/bin).
  • Enter the following command: ./java -d64 -Xms1024m -Xmx1024m -jar wls1211_generic.jar (WebLogic can be downloaded here).
  • Click next on the welcome screen.
  • Define the Middleware-Home directory and click next.
  • Fill in support credentials if applicable and click next.
  • Select custom as installation type and click next.
  • De-select the evaluation database and click next.
  • Select the installed JDK (if the JDK is not shown, click browse and browse to the root of the JDK) and click next.
  • Accept the defaults in the directories screen and click next.
  • Click next to start the installation.
  • De-select run quickstart and click done.

Next, we configure a domain. A WebLogic domain is a logical grouping of server instances that are controlled through an admin server. When creating a domain, our first step is to set-up the admin server, i.e., create the files that define the admin server. To this end we run the configuration wizard. Navigate to the ${WL_HOME}/common/bin directory and run config.sh:

  • Select Create a New WebLogic Domain.
  • Select Generate a Domain Configured Automatically to Support the Following Products: Basic WebLogic Server Domain 12.1.1.0 [wlserver_12.1].
  • Enter a domain name, for example base_domain.
  • Enter a username and password, for example weblogic and magic11g.
  • Choose a mode (for example, production mode) and select a JDK.
  • Note that the configuration wizard can create managed servers, cluster and machines.
  • Check the summary and click create.

When using WebLogic scripting tool, we can create a domain in production mode as follows:

beahome = '/home/oracle/weblogic12.1.1';
pathseparator = '/';
adminusername = 'weblogic';
adminpassword = 'magic11g';
domainname = 'base_domain';
domaintemplate = beahome + pathseparator + 'wlserver_10.3' + pathseparator + 'common' + pathseparator + 'templates' + pathseparator + 'domains' + pathseparator + 'wls.jar';
domainlocation = beahome + pathseparator + 'user_projects' + pathseparator + 'domains' + pathseparator + domainname;

readTemplate(domaintemplate);
setOption('DomainName', domainname);
setOption('OverwriteDomain', 'true');
setOption('ServerStartMode', 'prod');
cd('/Security/base_domain/User/weblogic');
cmo.setName(adminusername);
cmo.setUserPassword(adminpassword);
cd('/');
writeDomain(domainlocation);

In production mode the command-line will prompt for a username and password on start-up. To overcome this we add a boot.properties file. First create the directory ${DOMAIN_HOME}/servers/AdminServer/security and add a new file: boot.properties. Open the file in a text editor and add the following name-value pairs:

username=weblogic
password=magic11g

To start the admin server, open a command shell, navigate to the ${DOMAIN_HOME} directory and run startWebLogic.sh. The admin console can be reached at http://hostname:7001/console.

Set-up a clustered environment

To set-up the clustered environment we are going to use the following script:

userhome = '/home/oracle';
pathseparator = '/';

adminusername='weblogic';
adminpassword='magic11g';

jvmlocation = userhome + pathseparator + 'jrrt-4.0.1-1.6.0';

print 'CONNECT TO ADMIN SERVER';
connect(adminusername, adminpassword);

print 'START EDIT MODE';
edit();
startEdit();

print 'CREATE MACHINE';
machine = cmo.createMachine('TestMachine');
machine.getNodeManager().setNMType('ssl');

print 'CREATE CLUSTER';
cluster = cmo.createCluster('TestCluster');
cluster.setClusterMessagingMode('unicast');

print 'CREATE MANAGED SERVER: testSERVER1';
testServer1 = cmo.createServer('TestServer1');
testServer1.setListenPort(7002);
testServer1.setAutoRestart(true);
testServer1.setAutoKillIfFailed(true);
testServer1.setRestartMax(2);
testServer1.setRestartDelaySeconds(10);
testServer1.getServerStart().setJavaHome(jvmlocation);
testServer1.getServerStart().setJavaVendor('Oracle');
testServer1.getServerStart().setArguments('-jrockit -Xms1024m -Xmx1024m -Xns256m -Xgc:throughput');

print 'CREATE MANAGED SERVER: testSERVER2';
testServer2 = cmo.createServer('TestServer2');
testServer2.setListenPort(7003);
testServer2.setAutoRestart(true);
testServer2.setAutoKillIfFailed(true);
testServer2.setRestartMax(2);
testServer2.setRestartDelaySeconds(10);
testServer2.getServerStart().setJavaHome(jvmlocation);
testServer2.getServerStart().setJavaVendor('Oracle');
testServer1.getServerStart().setArguments('-jrockit -Xms1024m -Xmx1024m -Xns256m -Xgc:throughput');

print 'ADD MANAGED SERVERS TO CLUSTER';
testServer1.setCluster(cluster);
testServer2.setCluster(cluster);

print 'ADD MANAGED SERVERS TO MACHINE';
testServer1.setMachine(machine);
testServer2.setMachine(machine);

print 'CONFIGURE MIGRATION SERVICE';
cluster.setMigrationBasis('consensus');
cluster.setAdditionalAutoMigrationAttempts(3);
cluster.setMillisToSleepBetweenAutoMigrationAttempts(180000);
cluster.getDatabaseLessLeasingBasis().setMemberDiscoveryTimeout(30);
cluster.getDatabaseLessLeasingBasis().setLeaderHeartbeatPeriod(10);
candidatemachines = cluster.getCandidateMachinesForMigratableServers();
candidatemachines.append(machine);
cd('/Clusters/TestCluster');
set('CandidateMachinesForMigratableServers',candidatemachines);
cd('/');

print 'CONFIGURE MIGRATABLE TARGETS';
migratabletargets = cmo.getMigratableTargets();
for migratabletarget in migratabletargets:
	migratabletarget.setMigrationPolicy('exactly-once');
	cd('/MigratableTargets/' + migratabletarget.getName());
	set('ConstrainedCandidateServers',jarray.array([ObjectName('com.bea:Name=TestServer1,Type=Server'), ObjectName('com.bea:Name=TestServer2,Type=Server')], ObjectName))
	cd('/');

migratabletargetserver = migratabletargets[0];

print 'CREATE FILESTORE';
filestore = cmo.createFileStore('FileStore');
filestore.setDirectory('/home/oracle/weblogic12.1.1/deploy');
targets = filestore.getTargets();
targets.append(migratabletargetserver);
filestore.setTargets(targets);

print 'CREATE JMS SERVER';
jmsserver = cmo.createJMSServer('JMSServer');
jmsserver.setPersistentStore(filestore);
jmsserver.setTargets(targets);

print 'CREATE PATH SERVICE';
pathservice = cmo.createPathService('PathService');
pathservice.setPersistentStore(filestore);
pathservice.setTargets(targets);

targets.remove(migratabletargetserver);
targets.append(cluster);

print 'CREATE JMS SYSTEM MODULE';
module = cmo.createJMSSystemResource('SystemModule');
module.setTargets(targets);

targets.remove(cluster);
targets.append(jmsserver);

print 'CREATE SUBDEPLOYMENT';
module.createSubDeployment('SubDeployment');
subdeployment = module.lookupSubDeployment('SubDeployment');
subdeployment.setTargets(targets);

resource = module.getJMSResource();

print 'CREATE CONNECTION FACTORY';
resource.createConnectionFactory('ConnectionFactory');
connectionfactory = resource.lookupConnectionFactory('ConnectionFactory');
connectionfactory.setJNDIName('jms/ConnectionFactory');
connectionfactory.setDefaultTargetingEnabled(true);
connectionfactory.getDefaultDeliveryParams().setDefaultUnitOfOrder('.System');
connectionfactory.getTransactionParams().setTransactionTimeout(3600);
connectionfactory.getTransactionParams().setXAConnectionFactoryEnabled(true);

print 'CREATE UNIFORM DISTRIBUTED QUEUE';
resource.createUniformDistributedQueue('DistributedQueue');
distributedqueue = resource.lookupUniformDistributedQueue('DistributedQueue');
distributedqueue.setJNDIName('jms/CompanyQueue');
distributedqueue.setLoadBalancingPolicy('Round-Robin');
distributedqueue.setSubDeploymentName('SubDeployment');
distributedqueue.setUnitOfOrderRouting('PathService');

targets.remove(jmsserver);
targets.append(cluster);

print 'CREATE DATA SOURCE';
datasource = cmo.createJDBCSystemResource('DataSource');
datasource.setTargets(targets);
jdbcResource = datasource.getJDBCResource();
jdbcResource.setName('DataSource');
names = ['jdbc/exampleDS'];
dataSourceParams = jdbcResource.getJDBCDataSourceParams();
dataSourceParams.setJNDINames(names);
dataSourceParams.setGlobalTransactionsProtocol('LoggingLastResource');
driverParams = jdbcResource.getJDBCDriverParams();
driverParams.setUrl('jdbc:oracle:thin:@hostname:1521:SID');
driverParams.setDriverName('oracle.jdbc.OracleDriver');
driverParams.setPassword('password');
driverProperties = driverParams.getProperties();
driverProperties.createProperty('user');
userProperty = driverProperties.lookupProperty('user');
userProperty.setValue('username');
connectionPoolParams = jdbcResource.getJDBCConnectionPoolParams();
connectionPoolParams.setTestTableName('SQL SELECT 1 FROM DUAL');
connectionPoolParams.setConnectionCreationRetryFrequencySeconds(100);

print 'SAVE AND ACTIVATE CHANGES';
save();
activate(block='true');

print 'START EDIT MODE';
edit();
startEdit();

print 'CONFIGURE AUTOMATIC JTA MIGRATION';
cd('/Servers/TestServer1/JTAMigratableTarget/TestServer1');
set('ConstrainedCandidateServers',jarray.array([ObjectName('com.bea:Name=TestServer1,Type=Server'), ObjectName('com.bea:Name=TestServer2,Type=Server')], ObjectName));
cmo.setMigrationPolicy('failure-recovery');
cd('/');
cd('/Servers/TestServer2/JTAMigratableTarget/TestServer2');
set('ConstrainedCandidateServers',jarray.array([ObjectName('com.bea:Name=TestServer1,Type=Server'), ObjectName('com.bea:Name=TestServer2,Type=Server')], ObjectName));
cmo.setMigrationPolicy('failure-recovery');
cd('/');

print 'SAVE AND ACTIVATE CHANGES';
save();
activate(block='true');

The script:

  • Creates a machine called TestMachine.
  • Creates a cluster called TestCluster.
  • Creates two managed servers, TestServer1 and TestServer2, that are coupled to the TestMachine and TestCluster. The JVM is tuned by using -jrockit -Xms1024m -Xmx1024m -Xns256m -Xgc:throughput. By choosing throughput as optimization strategy the following defaults are present:
    • The compaction is configured as -XXcompaction:abortable=false,percentage=6.25,heapParts=4096,maxReferences=299900.
    • The thread local area size is configured as -XXtlasize:min=2k,preferred=16k,wastelimit=2k. Note that the preferred size depends on the heap size and lies between 16k and 64k.

    Additional tuning may be necessary when compaction causes long garbage collection pauses. To find out the impact compaction has on the garbage collection pause time, we can run a flight recording and examine the compaction pause parts of old garbage collections. In general, compaction pausetime depends on the compaction ratio (percentage or externalPercentage and internalPercentage) and the maximum number of references. In multi-threaded applications where threads allocate lots of objects, it might be beneficial to increase the TLA size. Caution must be taken, however, to not make the TLA size too large as this increases the fragmentation and as a result more garbage collections need to be run in order to allocate new objects. More information on tuning the JVM can be found in the post Fast, Faster, JRockit.

  • Configures a migration service that uses consensus based leasing. The migration policy is set to Auto-Migrate Exactly-Once Services which means that the service will run if at least one candidate server is available in the cluster. Note that this can lead to the case that all migratable targets are running on a single server. The migratable target performs health monitoring on the deployed migratable services and has a direct communication channel to the leasing system. When bad health is detected the migratable target requests the lease to be released in order to trigger a migration:
    • In the case of JTA, the server defaults to shutting down if the JTA system reports itself unhealthy, for example, if an I/O error occurs when accessing the default store. When a server fails, JTA is migrated to a candidate server.
    • In the case of JMS, the JMS server communicates its health to the monitoring system. When a dependent service such as a persistent store fails, for example due to errors in the I/O layer, it is detected by the migration framework. In this case the JMS server along with the persistent store (and path service when configured) is migrated to a candidate server.
  • Creates a JMS environment that consists of the following elements:
    • File Store – to persist messages that can be retrieved in the case of a server failure.
    • JMS Server – JMS servers act as management containers for the queues and topics in JMS modules that are targeted to them. A JMS server’s primary responsibility for its destinations is to maintain information on what persistent store is used for any persistent messages that arrive on the destinations, and to maintain the states of durable subscribers created on the destinations.
    • Path Service – A path service is persistent map that can be used to store the mapping of a group of messages in a Message Unit-of-Order to a messaging resource in a cluster. It provides a way to enforce ordering by pinning messages to a member of a cluster hosting servlets, distributed queue members, or store-and-forward agents. Note that the FileStore, JMSServer and PathService are all targeted to a migratable target.
    • JMS Module – JMS system resources are configured and stored as modules similar to standard Java EE modules. Such resources include queues, topics, connection factories, templates, destination keys, quota, distributed queues, distributed topics, foreign servers, and JMS store-and-forward (SAF) parameters. The JMS Module is targeted to the TestCluster.
    • JMS Resources:
      • Connection Factory is XA enabled and has the UnitOfOrder set to System. The connection factory is targeted directly to the JMS module.
      • Uniform Distributed Queue with a round-robin load balancing policy and has a unit of order routing that uses the PathService. Note that the uniform distributed queue is targeted to a SubDeployment that is targeted to the JMSServer.
  • Creates a data source that has global transactions enabled by using logging last resource.

The script can be executed by using:

[oracle@edu-wls-rh ~]$ cd weblogic12.1.1/wlserver_12.1/common/bin/
[oracle@edu-wls-rh bin]$ ./wlst.sh /home/oracle/weblogic12.1.1/deploy/scripts/create-environment.py 

CLASSPATH=/home/oracle/weblogic12.1.1/patch_wls1211/profiles/default/sys_manifest_classpath/weblogic_patch.jar:/home/oracle/weblogic12.1.1/patch_ocp371/profiles/default/sys_manifest_classpath/weblogic_patch.jar:/home/oracle/jrrt-4.0.1-1.6.0/lib/tools.jar:/home/oracle/weblogic12.1.1/wlserver_12.1/server/lib/weblogic_sp.jar:/home/oracle/weblogic12.1.1/wlserver_12.1/server/lib/weblogic.jar:/home/oracle/weblogic12.1.1/modules/features/weblogic.server.modules_12.1.1.0.jar:/home/oracle/weblogic12.1.1/wlserver_12.1/server/lib/webservices.jar:/home/oracle/weblogic12.1.1/modules/org.apache.ant_1.7.1/lib/ant-all.jar:/home/oracle/weblogic12.1.1/modules/net.sf.antcontrib_1.1.0.0_1-0b2/lib/ant-contrib.jar::/home/oracle/weblogic12.1.1/utils/config/10.3/config-launch.jar::/home/oracle/weblogic12.1.1/wlserver_12.1/common/derby/lib/derbynet.jar:/home/oracle/weblogic12.1.1/wlserver_12.1/common/derby/lib/derbyclient.jar:/home/oracle/weblogic12.1.1/wlserver_12.1/common/derby/lib/derbytools.jar::

Initializing WebLogic Scripting Tool (WLST) ...

Jython scans all the jar files it can find at first startup. Depending on the system, this process may take a few minutes to complete, and WLST may not return a prompt right away.

Welcome to WebLogic Server Administration Scripting Shell

Type help() for help on available commands

CONNECT TO ADMIN SERVER
Connecting to t3://localhost:7001 with userid weblogic ...
Successfully connected to Admin Server 'AdminServer' that belongs to domain 'base_domain'.

Warning: An insecure protocol was used to connect to the server. To ensure on-the-wire security, the SSL port or Admin port should be used instead.

START EDIT MODE
Location changed to edit tree. This is a writable tree with DomainMBean as the root. To make changes you will need to start an edit session via startEdit(). 

For more help, use help(edit)

Starting an edit session ...
Started edit session, please be sure to save and activate your changes once you are done.
CREATE MACHINE
CREATE CLUSTER
CREATE MANAGED SERVER: testSERVER1
CREATE MANAGED SERVER: testSERVER2
ADD MANAGED SERVERS TO CLUSTER
ADD MANAGED SERVERS TO MACHINE
CONFIGURE MIGRATION SERVICE
CONFIGURE MIGRATABLE TARGETS
CREATE FILESTORE
CREATE JMS SERVER
CREATE PATH SERVICE
CREATE JMS SYSTEM MODULE
CREATE SUBDEPLOYMENT
CREATE CONNECTION FACTORY
CREATE UNIFORM DISTRIBUTED QUEUE
CREATE DATA SOURCE
SAVE AND ACTIVATE CHANGES
Saving all your changes ...
Saved all your changes successfully.
Activating all your changes, this may take a while ...
The edit lock associated with this edit session is released once the activation is completed.
Activation completed
START EDIT MODE
Already in Edit Tree

Starting an edit session ...
Started edit session, please be sure to save and activate your changes once you are done.
CONFIGURE AUTOMATIC JTA MIGRATION
SAVE AND ACTIVATE CHANGES
Saving all your changes ...
Saved all your changes successfully.
Activating all your changes, this may take a while ...
The edit lock associated with this edit session is released once the activation is completed.

The following non-dynamic attribute(s) have been changed on MBeans that require server re-start:
MBean Changed : com.bea:Name=TestServer1,Type=Server
Attributes changed : Cluster, ListenPort, RestartDelaySeconds, AutoKillIfFailed, Machine, Cluster, ListenPort, RestartDelaySeconds, AutoKillIfFailed, Machine

MBean Changed : com.bea:Name=TestServer1,Type=JTAMigratableTarget,Server=TestServer1
Attributes changed : ConstrainedCandidateServers, ConstrainedCandidateServers, MigrationPolicy

MBean Changed : com.bea:Name=TestServer2,Type=JTAMigratableTarget,Server=TestServer2
Attributes changed : ConstrainedCandidateServers, ConstrainedCandidateServers, MigrationPolicy

MBean Changed : com.bea:Name=TestServer2,Type=Server
Attributes changed : Cluster, ListenPort, RestartDelaySeconds, AutoKillIfFailed, Machine, Cluster, ListenPort, RestartDelaySeconds, AutoKillIfFailed, Machine

Activation completed

Next, we will test if the set-up is working. To this end we need to configure the node manager. The node manager provides a mechanism to start and stop server instances, monitors the health of the servers and restarts servers in the case of failure. The node manager requires authentication to start and stop managed servers. The first time the node manager is started it communicates with the admin server to obtain a username and password that will be used by the admin server to authenticate the node manager. When the domain is created in production mode, random node manager credits are created. When we want to use, for example, nmConnect we have to set the node manager username and password to known values. To this end open the admin console:

  • Click on base_domain, security, general and click on the advanced link.
  • Edit the NodeManager Username and Password. In our case, we set these to respectively weblogic and magic11g.
  • Start the node manager by using the startNodeManager.sh script.

When the node manager is started use the admin console to start the managed servers. When everything is running the node managers and managed servers are properly configured, i.e., the node managers are initialized with the password files they need to accept commands from the admin server and when the managed servers are started on all candidate machines the node manager and domain directory are properly initialized.

To start the environment by using WLST we can use the following scripts. To start the node manager:

bea_home = '/home/oracle/weblogic12.1.1';
pathseparator = '/';
listen_port = '5556';
listen_address = 'localhost';

node_manager_home = bea_home + pathseparator + 'wlserver_12.1' + pathseparator + 'common' + pathseparator + 'nodemanager';

startNodeManager(verbose='true', NodeManagerHome=node_manager_home, ListenPort=listen_port, ListenAddress=listen_address);

To start the servers:

bea_home = '/home/oracle/weblogic12.1.1';
pathseparator = '/';
admin_username = 'weblogic';
admin_password = 'magic11g';
listen_address = 'localhost';
listen_port = '5556';
admin_server_url='t3://localhost:7001'
domain_name = 'base_domain';
domain_home = bea_home + pathseparator + 'user_projects' + pathseparator + 'domains' + pathseparator + domain_name;

print 'CONNECT TO NODE MANAGER';
nmConnect(admin_username, admin_password, listen_address, listen_port, domain_name, domain_home, 'ssl');

print 'START ADMIN SERVER ONLY ON THE MACHINE WHERE THE ADMIN SERVER IS PRESENT';
nmStart('AdminServer');

print 'CONNECT TO ADMIN SERVER';
connect(admin_username, admin_password, admin_server_url);

print 'START MANAGED SERVERS ON THE MACHINE';
start('TestServer1','Server');
start('TestServer2','Server');

To stop the node manager:

bea_home = '/home/oracle/weblogic12.1.1';
pathseparator = '/';
admin_username = 'weblogic';

admin_password = 'magic11g';
listen_address = 'localhost';
listen_port = '5556';

domain_name = 'base_domain';
domain_home = bea_home + pathseparator + 'user_projects' + pathseparator + 'domains' + pathseparator + domain_name;

nmConnect(admin_username, admin_password, listen_address, listen_port, domain_name, domain_home, 'ssl');
stopNodeManager();

To stop the servers:

bea_home = '/home/oracle/weblogic12.1.1';
pathseparator = '/';
admin_username = 'weblogic';
admin_password = 'magic11g';
listen_address = 'localhost';
listen_port = '5556';
admin_server_url='t3://localhost:7001'
domain_name = 'base_domain';
domain_home = bea_home + pathseparator + 'user_projects' + pathseparator + 'domains' + pathseparator + domain_name;

print 'CONNECT TO NODE MANAGER';
nmConnect(admin_username, admin_password, listen_address, listen_port, domain_name, domain_home, 'ssl');

print 'CONNECT TO ADMIN SERVER';
connect(admin_username, admin_password, admin_server_url);

print 'STOPPING SERVERS ON THE MACHINE';
shutdown('TestServer1','Server','true',1000,'true');
shutdown('TestServer2','Server','true',1000,'true');

print 'STOPPING ADMIN SERVER ONLY ON THE MACHINE WHERE THE ADMIN SERVER IS PRESENT';
shutdown('AdminServer','Server','true',1000,'true');

Create shell scripts that run the scripts, for example,

#!/bin/sh

BEA_HOME="/home/oracle/weblogic12.1.1"
export BEA_HOME

WL_HOME="/home/oracle/weblogic12.1.1/wlserver_12.1"
export WL_HOME

. ${WL_HOME}/common/bin/wlst.sh ${BEA_HOME}/deploy/scripts/startNodeManager.py

My first JavaEE6 application

The application uses Java Persistence and Java Messaging, i.e., we have the following persistence.xml file

<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="PersonPersistenceUnit">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <jta-data-source>jdbc/exampleDS</jta-data-source>
        <class>model.entities.Person</class>
    </persistence-unit>
</persistence>

We use EclipseLink as the persistence provider and use a JTA-enabled data source. We have the following stateless enterprise bean:

package model.logic;

import model.entities.Person;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.jms.*;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless(name = "Company", mappedName = "ejb/Company")
public class CompanyBean implements Company {

    @PersistenceContext(unitName = "PersonPersistenceUnit")
    private EntityManager entityManager;

    @Resource(name = "jms/ConnectionFactory")
    private ConnectionFactory connectionFactory;
    @Resource(name = "jms/CompanyQueue")
    private Queue destination;

    private Connection connection = null;
    private Session session = null;
    private MessageProducer messageProducer = null;

    @PostConstruct
    public void init() {
        try {
            connection = connectionFactory.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            messageProducer = session.createProducer(destination);
        } catch (JMSException e) {
            if (connection != null) {
                try {
                    connection.close();
                } catch (JMSException f) {
                    e.printStackTrace();
                }
            }
        }
    }

    @PreDestroy
    public void release() {
        try {
            if (connection != null) {
                connection.close();
            }
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    public void insertPerson(Person person) {
        if (findPerson(person.getSofinummer()) == null) {
            entityManager.persist(person);
            sendMessage(person);
        } else {
            updatePerson(person);
        }
    }

    public void removePerson(Integer sofinummer) {
        Person person = findPerson(sofinummer);
        if (person != null) {
            entityManager.remove(entityManager.merge(person));
            sendMessage(person);
        }
    }

    public void updatePerson(Person person) {
        entityManager.merge(person);
    }

    private Person findPerson(Integer sofinummer) {
        return entityManager.find(Person.class, sofinummer);
    }

    private void sendMessage(Person person) {
        try {
            ObjectMessage message = session.createObjectMessage();
            message.setObject(person);
            messageProducer.send(message);
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

We inject an entity manager, JMS connection factory and JMS queue into the bean. By using the EJB lifecycle (PostConstruct) we set-up a JMS message producer (note that by using PreDestroy the created resources are cleaned-up). We accept the defaults for the transaction manager, i.e., required. The method insertPerson, inserts a person into the database and sends a message to the queue, if the person does not exist. If on the other hand, the person does exist the person information is updated in the database. Note that we are using two resources, which means that we have to resort to a two-phase commit. In the server set-up this was configured by using the logging last resources (emulates a two phase commit) and by using XA for the JMS connection factory. For the stateless enterprise bean we have defined the following remote interface:

package model.logic;

import model.entities.Person;

import javax.ejb.Remote;

@Remote
public interface Company {
    public void insertPerson(Person person);

    public void removePerson(Integer sofinummer);

    public void updatePerson(Person person);
}

We have the following message-driven bean:

package model.logic;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.*;

@MessageDriven(mappedName = "jms/CompanyQueue", activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationName", propertyValue = "jms/CompanyQueue"),
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
})
public class CompanyMDB implements MessageListener {

    public void onMessage(Message message) {
        if (message instanceof ObjectMessage) {
            ObjectMessage objectMessage = (ObjectMessage) message;
            try {
                System.out.println("RECEIVED OBJECT MESSAGE " + objectMessage.getObject());
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }

        if (message instanceof TextMessage) {
            TextMessage textMessage = (TextMessage) message;
            try {
                System.out.println("RECEIVED TEXT MESSAGE " + textMessage.getText());
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

The message-driven bean is configured to listen to the jms/CompanyQueue (which is a distributed queue). The following servlet is used for testing:

package userinterface.servlets;

import model.entities.Person;
import model.logic.Company;

import javax.ejb.EJB;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;

@WebServlet(name = "TestServlet", urlPatterns = "/testservlet")
public class TestServlet extends HttpServlet {

    @EJB(name = "Company", mappedName = "ejb/Company")
    Company company;

    private Random generator = null;

    @Override
    public void init() throws ServletException {
        generator = new Random();
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Person person = createPerson();

        if (generator.nextDouble() &lt; 0.01) {
            company.removePerson(person.getSofinummer());
        } else {
            company.insertPerson(person);
        }
    }

    @Override
    public void destroy() {
        generator = null;
    }

    private Person createPerson() {
        Person person = new Person();
        person.setNaam(Long.toString(Math.abs(generator.nextLong()), 36));
        person.setSofinummer(generator.nextInt(10000));
        return person;
    }
}

Note that the servlet is configured by using the WebServlet annotation. We inject the stateless enterprise bean by using the EJB annotation. Package the application as follows:

LoadTest6.ear
	META-INF
		application.xml
	Model.jar
		META-INF
			ejb-jar.xml
			persistence.xml
		packages
	Web.war
		WEB-INF
			classes
				packages
			web.xml

in which the deployment descriptors are defined as follows:

application.xml

<application xmlns="http://java.sun.com/xml/ns/javaee"
             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/application_6.xsd"
             version="6">
    <module id="Model">
        <ejb>Model.jar</ejb>
    </module>
    <module id="Web">
        <web>
            <web-uri>Web.war</web-uri>
            <context-root>LoadTest6</context-root>
        </web>
    </module>
</application>

ejb-jar.xml

<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"
         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"
         version="3.1">
</ejb-jar>

web.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
           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/web-app_3_0.xsd"
           version="3.0">
</web-app>

One thing to note is that the deployment descriptors ejb-jar.xml and web.xml are empty, most configuration can in JavaEE6 be accomplished by using annotations. In order to build the application we need javaee-api-6.0.jar on the class path, which can be downloaded here.

Testing

We test the environment by using the The Grinder. The following script calls the application:

from net.grinder.script.Grinder import grinder
from net.grinder.script import Test
from net.grinder.plugin.http import HTTPRequest

test1 = Test(1, "Request resource")
request1 = test1.wrap(HTTPRequest())

class TestRunner:
    def __call__(self):
        result = request1.GET("http://172.31.0.113:7777/LoadTest6/testservlet")

Before, we start the test we configure a load balancer. The load balancer decides how to dispatch requests to the back end server instances. A load balancer needs to do tasks such as distributing requests, health checking and session binding. To load balance requests, we are going to use the web server plug-in. To configure the web server plug-in (mod_wl_ohs), open the mod_wl_ohs.conf file and add the following contents:

LoadModule weblogic_module   "${ORACLE_HOME}/ohs/modules/mod_wl_ohs.so"

<IfModule weblogic_module>
	ConnectTimeoutSecs 10
	ConnectRetrySecs 2
	DebugConfigInfo ON
	WLSocketTimeoutSecs 2
	WLIOTimeoutSecs 300
	Idempotent ON
	FileCaching ON
	KeepAliveSecs 20
	KeepAliveEnabled ON
	DynamicServerList ON
	WLProxySSL OFF
</IfModule>

<Location /LoadTest6>
	SetHandler weblogic-handler
	WebLogicCluster 172.31.0.113:7002,172.31.0.113:7003
</Location>

WebLogic offers an extensive diagnostics framework. Each created resources has its own monitoring environment that gives information how the runtime of a certain sub-system is doing. This information can also be displayed graphically by using the WLDF console extension, which can be enabled in the admin console (preferences, extensions tab and enable the diagnostics console extension – to make the changes active the server has to be restarted). The monitoring dashboard can be reached at: http://hostname:7001/console/dashboard. For example, to view how the memory of the individual JVMs are doing we can start the JVM Runtime Heap view:

It is also possible to create our own views, for example, say we are interested in the current number of connections in the connection pool and want to know if requests are waiting for connections. To this end, click on my views and click on the new view icon. To add graphs to our view click on the metric browser tab, select the TestServer1, select the JDBCConnectionPool, DataSource, and drag and drop the metrics ActiveConnectionsCurrentCount and WaitingForConnectionCurrentCount on the view:

References

[1] WebLogic Server 12c Release 1 (12.1.1) Documentation Library
[2] The Java EE 6 Tutorial