The article originally appeared on BEA's dev2dev
developer portal. Visit dev2dev for
more articles on this topic.
By: Philip Aston
Introduction
The WebLogic Scripting Tool is a BEA tool for scripting the
configuration of BEA WebLogic domains. WLST is currently available as
a developer release for WebLogic 8.1 on the BEA dev2dev web site. BEA plans to
include WLST as part of the next major release of WebLogic Server.
Please note that neither the dev2dev release of WLST nor the
code included with this paper is covered by BEA Support.
I've helped build a number of custom configuration systems over
the years. In spite of its developer pre-release status, WLST is more
suited to WebLogic configuration than other options available to date.
Before WLST, WebLogic users typically would apply a combination of
Apache Ant, shell scripts, XSLT, the WebLogic Configuration Wizard,
and the tool most akin to WLST, Paco Gomez's wlshell. I
now recommend WLST as the tool of choice to my customers.
Why is WLST so much better than wlshell? The secret
of its power is its scripting language, Jython, an implementation of the popular
Python language in Java.
WLST is available in two flavors:
- WSLT Online: can be used to query and modify a running WebLogic Server domain.
- WLST Offline: can be used to build WebLogic domains without requiring a running WebLogic Server domain.
This article focuses solely on using WLST Offline. When combined
with a source control system, WLST Offline scripts are a great way to
record and manage configuration. Much could be said about using WLST
Online as a multitool for slicing and dicing live WebLogic domains.
I'll leave that for others to cover. However, much of my
advice is equally applicable to WLST Online, and I'll refer to WLST
Offline simply as WLST from now on.
The pre-release of WLST can be forgiven for having a few rough
edges and, dare I say it, one or two bugs. This paper contains tips
and tricks I've found useful in overcoming these problems and also how to
apply the power of Jython and WLST to prepare production-quality
WebLogic domains. This article does not attempt to teach the reader
Jython or WLST. Jython is easy to learn for Java or shell script
programmers (although they might use it in very different styles).
Similarly, this is not a tutorial for newcomers to WLST. To get the greatest
benefit from this material, you'll need
to familiarize yourself with Jython and WLST.
Example source code
This article is accompanied by example source code that illustrates
many of the techniques discussed below. The code has been tested with
the pre-release of WLST Offline and WebLogic Server 8.1. I expect it
will need minor updates to function with the final version of
WLST.
If you want to run the example code, be sure you have the latest
version of WLST Offline from dev2dev. If you experience
errors such as Error: cd() failed, you're using an older
version.
Tip: Accessing WLST from Other Modules
WLST scripts interact with the WLST engine through several
functions and variables, for example, assign(), cmo, andreadTemplate(). These are implicitly made available to a
script invoked though WLST, that is, they are automatically present in the
script's global namespace. This is convenient for scripts as they do
not need to explicitly import the objects.
Unfortunately, WLST does not allow submodules to import these
objects. This hinders our ability to factor scripts appropriately into
modules. A simple but ugly solution would be to pass these objects as
parameters to every function that required them.
Here's a better solution. Define a method in awlstutility package that reads the required objects from
a dictionary and stores it in the wlstutility global
namespace. Each of our top-level scripts begins with:
import wlstutility wlstutility.initialize(globals())
The initialize method is defined in the wlstutility package's __init__.py file.
# __all__ defines the public names that the package
# exports.
__all__ = <>
def initialize(topLevelNamespace):
for f in ("assign", "cd", "create", "ls",
"readTemplate", "set", "writeDomain"):
globals() = topLevelNamespa ce __all__.append(f)
global _topLevelNamespace
_topLevelNamespace = topLevelNamespace
def getCMO():
return _topLevelNamespace<"cmo"> Then, in any other module, import the required objects from wlstutility:
from wlstutility import assign, cd, ls, set, getCMO
Note that access to the WLST cmo variable has been replaced with a getCMO() function, which is better form.
The ability to use WLST from other modules will be added in a
future release of WLST. A writeIniFile() method will be
added that creates a Python module containing the WLST defined
functions and variables.
Python Power: MBean Constructors
Standard WLST style involves repetitive calls to MBeans:
cd("/")
jmsTemplate = create("myTemplate", "JMSTemplate")
jmsTemplate.redeliveryDelayOverride = 100
jmsTemplate.redeliveryLimit = 3
# ...We also have to make sure we use the correct type name in the argument to create. Wouldn't it be nicer if we had a constructor for each type of MBean? It could be something like:
from wlstutility.constructors import JMSTemplate
cd("/")
jmsTemplate = JMSTemplate("myTemplate",
redeliveryDelayOverride = 100,
redeliveryLimit = 3)This certainly looks prettier to me. You might need to configure
something more complex such as a JDBCConnectionPool
before you appreciate the full benefits. MBean constructors have the
additional advantage of allowing sets of parameters to be passed around
using dictionaries:
myRedeliveryPolicy = { "redeliveryDelayOverride" : 100,
"redeliveryLimit" : 3 }
cd("/")
jmsTemplate = JMSTemplate("myTemplate",
bytesMaximum = 1000000,
**myRedeliveryPolicy)Here's the code for creating these MBean constructors:
# wlstutility/constructors.py
import wlstutility
__all__ = <>
for mbeanType in (
"Application", "Cluster", "Domain", "DomainLogFilter",
"EJBComponent", "EmbeddedLDAP", "ExecuteQueue",
"JDBCConnectionPool", "JDBCDataSource",
"JDBCTxDataSource", "JMSConnectionFactory",
"JMSDistributedQueue", "JMSDistributedQueueMember",
"JMSDistributedTopic", "JMSDistributedTopicMember",
"JMSFileStore", "JMSQueue", "JMSServer", "JMSTemplate",
"JMSTopic", "JTA", "Log", "Machine", "MailSession",
"SecurityConfiguration", "Server", "StartupClass",
"SSL", "UnixMachine", "User", "WebAppComponent",
"WebServer", ):
def mbeanFactory(name, _type=mbeanType,
existingMBean = None, **kwds):
logMBeanCreation(name, _type, kwds)
if existingMBean:
mbean = existingMBean
existingMBean.name = name
else:
mbean = wlstutility.create(name, _type)
for key, value in kwds.items():
setattr(mbean, key, value) return mbean
globals() = mbeanFactory
__all__.append(mbeanType) The code iterates through a number of known MBean types. For each type it creates a new functionmbeanFactory, which carries out the business of creating an instance of the correct type and
applying any keyword arguments by invoking the appropriate set method. The function is then inserted
into the module's global namespace and appended to the list of names that the module exports
(__all__). The new functions can be imported from other modules
as shown in the examples above.
What does the existingMBean argument do? It allows you to use the "constructors" with
existing MBeans. For example:
from wlstutility import getCMO
from wlstutility.constructors import ExecuteQueue
# ...
cd("/")
cd("Servers/%s/ExecuteQueue/weblogic.kernel.Default" % myServerName)
defaultQueue = ExecuteQueue("weblogic.kernel.Default",
existingMBean = getCMO(),
threadCount = 15)WLST Idiosyncrasy: The Template Must Contain At Least One Server
When using WLST Offline you start with a Configuration Wizard
Domain Template. WLST Offline expects
the domain template to contain at least one server entry. It treats
the first server as the administration server for the domain. We want
the flexibility to define as many or as few servers as we like, and we
don't want to end up with an additional server inherited from the
template.
To work around this, we define a single server in our domain
template. This will become the administration server. We then change
its name appropriately using the existingMBean support in the
utility constructors.
from wlstutility import getCMO
from wlstutility.constructors import Server
# ...
# Our domain template has a blank server called "admin"
cd("/")
cd("Servers/admin")
# The following statement uses the existingMBean argument to alter the
# existing MBean, rather than defining a new one.
administrationServer = Server(domainName,
"MyAdministrationServer", # Rename
existingMBean = getCMO(),
administrationPort = 7001,
# ...
)Tip: You've Got an Object Model—Use It!
WLST works against the JMX object model. You rarely have to keep
your own model of the configuration; just pass MBeans around
instead. Here's an example of configuring a JDBCConnectionPool and DataSource.
# (server MBean created above).
cd("/")
pool = JDBCConnectionPool("Pool_%s" % server.name,
URL = url,
**nonXAPoolOptions)
dataSource = JDBCDataSource("DataSource_%s" % server.name,
poolName = pool.name,
# ...
)Note how we refer to the name attributes of other
MBeans in the constructor calls.
WLST Idiosyncrasy: No Script Parameters
It's useful to be able to pass
command-line parameters to the script. This isn't possible with WLST as it
treats multiple arguments as multiple scripts. It would be better if
it just treated the first argument as a script and passed the
remaining arguments in sys.argv. Also, it would be more Pythonic if the__name__ of the top-level module was__main__.
Unfortunately there's no clean work around for these problems. To customize the behavior of scripts,
I use a properties file with a name that's hard-coded in the script.
The sample code provides a loadProperties method that
wraps up the loading of these properties into a Python dictionary.
This will be addressed in a future release of WLST. The first
command-line parameter will be treated as a script name, and the
remaining arguments will be passed to the script.
WLST Idiosyncrasy: writeDomain Renames the Domain
When writeDomain() is called, the names for
Domain, JTA, SecurityConfiguration, and
EmbeddedLDAP MBeans are ignored. The name of the output
directory is used instead.
I usually want the names of these MBeans to match the domain name,
so I pass the domain name as the directory parameter towriteDomain() and rename the directory afterward if
necessary.
This will be addressed in a future dev2dev release of WLST. The domain
name will be able to be set separately from the domain directory
name.
Python Power: Dealing with JMS Distributed Destinations
Let's consider something a little more complex. Suppose we have a
number of application JMS queues that need to be deployed to a cluster
as WebLogic distributed destinations. This requires configuring
several types of MBean for each application queue:
- A
JMSQueuetargeted to each server in the cluster - A
JMSDistributedQueuetargeted to the cluster - A
JMSDistributedDestinationMemberassociating eachJMSQueuewith theJMSDistributedQueue
We'll also need to be able to create similar MBeans for distributed
topics and wire up the references to error destinations.
In the following code, the configuration of an individual queue or topic is wrapped in aDestinationFactory. A DestinationFactory
knows how to create the appropriate MBeans for the application code.
Two additional classes, QueueFactory andTopicFactory, are DestinationFactory
subclasses that specialize its behavior appropriately. This is how DestinationFactories are
used:
destinationFactories = (
QueueFactory("AlphaQueue",)
QueueFactory("ErrorQueue", distributed = 0),
QueueFactory("BetaQueue", errorDestination = "ErrorQueue"),
TopicFactory("MyTopic"))
for destinationFactory in destinationFactories:
destinationFactory.createDistributedDestination(cluster)
for server in managedServers:
cd("/")
fileStore = JMSFileStore("JMSFileStore_%s" % server.name,
directory = "jmsfilestore")
jmsServer = JMSServer("JMSServer_%s" % server.name,
store = fileStore)
assign("JMSServer", jmsServer.name, "Target", server.name)
for destinationFactory in destinationFactories:
destinationFactory.createDestinationForServer(
server, jmsServer)I don't have space to discuss the DestinationFactory
implementation here, but you should be able to understand how they
function from the example.
Tip: Post-Processing the Output
WLST only understands a specific set of tokens in template files,
for example @BEAHOME. It replaces these tokens with
environment-specific values based on the stringsubs.xml
descriptor file in the template. Your script templates most likely
will have additional parameters that do not belong to the set that
WLST sets. I post-process the generated files to set these parameters
appropriately.
Additionally, there are occasional MBean/WLST bugs that can be
worked around by post-processing. Here are a couple I encountered:
Setting the ApplicationMBeantwoPhase attribute
WLST has a defect such that it considers the default value oftwoPhase for an ApplicationMBean to betrue. The default value is actually false.
As WLST only generates configuration for non-default values, it is
impossible to set twoPhase to be true.
To work around this, set twoPhase tofalse so that an attribute is written out, and
post-process config.xml to change all instances ofTwoPhase='false' toTwoPhase='true'.
Support for the full Oracle JDBC URL syntax
WLST does not accept Oracle URLs of the form: jdbc:oracle:thin:@//host:1234/SERVICE.myco.com, that is, where an Oracle service name is specified rather than a SID.
To work
around this, generate a placeholder URL and post-processconfig.xml to insert the real URLs.
Conclusion
This article looks at several aspects of WLST. It shows a number of WLST
idiosyncrasies, provides tips for effectively using WLST, and notes how to
take advantage of the Python underpinnings. Jython can be used for many other purposes, for example,
packaging up the resulting domain into individual tar files for each
server that can be more easily copied to the target environment.
Fortunately, Jython places both Java and Python libraries at your
fingertips, so this is easy to do.
I hope I've encouraged you to increase the level of automated
scripting you use to configure your WebLogic domains.
Acknowledgements
Many thanks to Matthew Slingsby and to Larry Du and the WLST team for their input.
References
- WLST Offline
- WLST Online
- CodeShare project hosting sample WLST scripts
- Jython
- Python documentation
config.xmlreference
Philip Aston is a senior principal consultant for BEA Services, specializing in WebLogic Server. He also maintains The Grinder, an open source load testing tool.
This article is copyright 2005 and is reprinted with the written
permission of dev2dev
/ciol/media/agency_attachments/c0E28gS06GM3VmrXNw5G.png)
Follow Us