November 18, 2009

WebLogic Server Shared Libraries For Static Resources

In the OTN WLS General forum someone asked a question regarding using Shared Libraries to share static web resources among applications.  “Basically, we are trying to load jquery, css files, shared images, and what not into a shared library for access from other Web Applications.”  Shared libraries will handle this use case well.  I’ve put together a basic example using a simple image that is put into a shared library that is referenced by another web application.  The image in the screen shot below is from a shared library.  The web application that references it simply contains the JSP and a reference to the shared library in the weblogic.xml deployment descriptor.

SharedLibExample

Basic Steps

  1. Assemble the resources (images, css, etc) in a base directory, with subdirectories if desired
  2. In the main directory, create a META-INF/MANIFEST.MF file describing the library
  3. Package the base directory as a WAR file
  4. Deploy the WAR as a library to WLS
  5. Refer to the Shared Library in a web application’s WEB-INF/weblogic.xml
  6. Refer to the static resources from the library in the web application as if they are local to your application

Here is the link to the full documentation that describes all of the options for Shared Libraries in WebLogic.

Simple Example

I recommend taking a look at the shared libraries that ship with WebLogic Server to use as an example.  In WLS 10.3.x they are located here: <MIDDLEWARE_HOME>\wlserver_10.3\common\deployable-libraries

Here is what my META-INF/MANIFEST.MF file looks like for my example, I simply copied an existing MANFIEST.MF from jsf-1.2.war and made edits:

Manifest-Version: 1.0
Specification-Title: Images
Specification-Version: 1.0
Implementation-Title: Images Implementation
Implementation-Version: 1.0
Implementation-Vendor: Oracle
Extension-Name: images

Here is what the packaging of the library looks like after zipping it up as a WAR file:

C:\temp\images>jar -tf images.war
META-INF/
META-INF/MANIFEST.MF
oralogo_small.gif

As you can see, it’s only a simple WAR file with an oracle image in the base directory with a META-INF/MANIFEST.MF file describing the library.  To deploy this to the WLS, you can do that as normal other than noting that it is a library.  After doing that it will show up like this in the console:

deployments

If you’re using Oracle Enterprise Pack for Eclipse and set your Windows->Preferences->Server->Runtime Environments correctly, then the existing shared libraries should be available in Preferences->WebLogic->Shared Libraries and you can add the library you just created to the list.  This will allow you to edit the WEB-INF/weblogic.xml in the IDE with a dialog box, correctly specify the reference to the shared library, and deploy the shared library for you to the server if it has not been done already when you deploy the application.

weblogic.xml

<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app" 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_2_5.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd">
    <wls:weblogic-version>10.3.2</wls:weblogic-version>
    <wls:context-root>ImagesWeb</wls:context-root>
    <wls:library-ref>
        <wls:library-name>images</wls:library-name>
        <wls:specification-version>1.0</wls:specification-version>
        <wls:exact-match>true</wls:exact-match>
    </wls:library-ref>
</wls:weblogic-web-app>

Now in the index.jsp, I can simply refer to the image as if it were in my base directory of the application like this:

<img src="oralogo_small.gif"/>

Conclusion

Shared libraries are usually used to share code such as jars and class files in WARs and EARs, but they can also be used to share static resources.  So let’s say I wanted to share a jar file, I would have a WEB-INF/lib directory in my shared library, and put any jars that I wanted my application’s to have in there.  If it is an EAR file, then I would use APP-INF/lib.

You can download this extremely simple example here.  I tested this with WLS 10.3.2 also known as 11g PS1.

November 5, 2009

Oracle’s Brian Oliver on Data Grid Design Patterns

Last week I wrote about my experiences playing with the Coherence Inbator’s Push Replication Pattern and showed some screenshots of the JMX instrumentation with JConsole and my post is already out of date.  This week there has been a new release of the Coherence Incubator projects.   Another new development is that you now learn directly from the lead of the Coherence Incubator projects as InfoQ just posted a video presentation from Brian Oliver discussing several of the projects and the rationale behind them.  One announcement I’m particularly fond of is the new Examples project.  This should help jump-start those getting started with the Coherence Incubator as in prior releases the examples were in separate places and not as complete.  Cheers to the Incubator team for continuing to improve and share these patterns.

October 22, 2009

Synchronizing Coherence Clusters – A Tour of Push Replication

Lately I've been able to do some Coherence work with some local customers and play with the Coherence Incubator projects.  This entry will showcase one of the examples for Push Replication for sharing data among separate Coherence clusters.

Inter-cluster Data Replication

Coherence clustering technology makes a lot of sense for customers to be able to scale applications horizontally and reliably with very fast predictable performance.  This is of course easy to do within one data center on a fast network.  One of the common challenges that customers have is around High Availability and Disaster Recovery and keeping data synchronized across data centers.  Normally Coherence is optimized to use UDP unicast or multicast, but what happens if the network is unreliable and/or has high latency which is common when networking multiple data centers?  The answer is Coherence TCP Extend, which addresses these challenges by using TCP and the result is that multiple Coherence clusters can communicate together.

Multiple data centers are not the only reason you need to keep multiple clusters in sync.  I recently came across a use case where it made sense to have separate Coherence clusters on different physical machines.  The JVM processes for each machine have a single digit millisecond SLA for round trip time per request, so they are very sensitive to any outside events and it was a requirement to isolate the processes as much as possible.  In this scenario one physical machine was Active and one was Passive for fail-over.  During load-testing on the Active machine we found that bringing up cluster members on the Passive machine in the middle of the test impacted performance when both machines were part of the same cluster.  Using a single Coherence cluster per machine ensures that the impact of cluster membership events are isolated to that machine only.

Leveraging the Coherence Incubator Push Replication pattern, multiple Coherence clusters can keep data in sync whether they are on the same network subnet with low-latency or hundreds of miles apart in separate data centers.  Let's take a look at the simplest example, which is Active - Passive scenario.  In this situation we'll use Push Replication to make sure that Cache Entry operations (insert/update/delete) in the Active cache are replicated in the Passive cache.

Incubator-PushReplication-Active-Passive

Run the Example

To run this example called ActivePassiveExample - which is included in the src distribution of the Push Replication Pattern, I used the following software:

The Ant Approach

One of my colleagues Randy Stafford introduced me using an ant build.xml file to organize different types of Coherence cluster processes.  I found it to be a much simpler and cleaner approach than what I had been doing (multiple shell scripts or multiple Eclipse launch configurations).  This way it is simple to centralize the shared configuration to a set of properties that are used by all of the ant targets and each process can easily override one of the properties with a -DpropertyName=propertyValue at the command line.  Take a look at the build.xml file and let me know what you think.

Step by Step

  • Set the build.properties paths according to your environment
  • Set up shells with ANT_HOME and JAVA_HOME and the PATH, setAntEnv.txt is an example
  • Run ant from those shells with the appropriate targets as described in build.xml.  Minimum for this example:
    ant compile (you can reuse this shell)
    ant run_active_cache_server
    ant run_passive_cache_server
    ant run_active_publisher
  • Most likely you will also want to run the JMX and Console processes to see the values.
    ant run_active_jmx
    ant run_passive_jmx
    ant run_passive_console

Guided Tour

Once the JMX processes are running for Active and Passive, you can use jconsole to locally bind to the two MBeanConnector process.

cluster

To verify that the passive cache received all the updates you can execute the run_passive_console target and execute two commands to see the contents.

  • cache passive-cache
  • list

Notice that at the bottom of my output that the entry values are the last ten number leading up to 10000, which is what we expect looking at the sample publishing code.

     [java]   ClusterService{Name=Cluster, State=(SERVICE_STARTED, STATE_JOINED), Id=0, Version=3.5, OldestMemberId=1}
     [java]   InvocationService{Name=Management, State=(SERVICE_STARTED), Id=1, Version=3.1, OldestMemberId=1}
     [java]   )
cache passive-cache
     [java] 2009-10-22 17:07:31.797/10.344 Oracle Coherence GE 3.5.1/461p2 <Info> (thread=Main Thread, member=3): Loaded cache configuration from "jar:file:/C:/Oracle/coherence-v3.5.1b461/coherence/lib/coherence.jar!/coherence-cache-config.
xml"
     [java] 2009-10-22 17:07:32.156/10.703 Oracle Coherence GE 3.5.1/461p2 <D5> (thread=DistributedCache, member=3): Service DistributedCache joined the cluster with senior service member 1
     [java] 2009-10-22 17:07:32.172/10.719 Oracle Coherence GE 3.5.1/461p2 <D5> (thread=DistributedCache, member=3): Service DistributedCache: received ServiceConfigSync containing 259 entries
     [java] <distributed-scheme>
     [java]   <scheme-name>example-distributed</scheme-name>
     [java]   <service-name>DistributedCache</service-name>
     [java]   <backing-map-scheme>
     [java]     <local-scheme>
     [java]       <scheme-ref>example-binary-backing-map</scheme-ref>
     [java]     </local-scheme>
     [java]   </backing-map-scheme>
     [java]   <autostart>true</autostart>
     [java] </distributed-scheme>
     [java] Map (?):
list
     [java] 2 = 9992
     [java] 3 = 9993
     [java] 1 = 9991
     [java] 0 = 10000
     [java] 5 = 9995
     [java] 4 = 9994
     [java] 6 = 9996
     [java] 8 = 9998
     [java] 9 = 9999
     [java] 7 = 9997
     [java] Map (passive-cache):

Let's look at the Active cluster processes.  You can see that there are caches defined for many of the Incubator patterns.  Below you can see that the publishing-active cache should have a size of 10 after running the run_active_publisher ant target.

publishingCacheStore

Consider the role of some of the other caches:
DistributedCacheForCommandPattern - stores command contexts and co-located commands


DistributedCacheForMessages - stores messages (in this case - entries that need to be pushed to the other cluster)


DistributedCacheForSubscriptions - maintains durable subscribers for the publishers

You can learn more about these caches by looking at the Coherence Incubator Wiki and reviewing the cache-config xml files that correspond to the Incubator project they are named for.

Looking at the MBean for the PublishingService, you can find operations to suspend, resume, and drain messages that have yet to be published.

publishingService [2]

So if you suspend() the PublishingService, any changes to the publishing-active cache will be queued up as messages in the DistributedCacheForMessages coherence.messagingpattern.messages cache.  You can try this by executing the suspend() operation then the run_active_publisher target again.  You should then see the messaging cache full of the operations waiting to be replicated to the passive-cache.

messagesCache

Now if you execute the resume() operation on the PublisherService, the messages will replicate to the passive-cache.  Alternatively you could execute the drain() operation and all of the messages waiting to be replicated would be deleted and the passive-cache would never receive those updates.

On the Passive cluster, you can see the TCP Extend working by looking at the ConnectionManager and the Connection MBeans.  The passive process is listening in this case on port 20000 and has 1 active connection.

tcp_extend

Conclusion

This should give you a quick idea of the capabilities of the Push Replication Pattern and some of the JMX enabled capabilities in the Coherence Incubator projects.  This is the most basic example and there are also samples for Hub-and-Spoke, Active-Active, and Federated patterns which are more complex.

September 8, 2009

Deployment Plan Example for WebLogic Server Part 2

I had some people contact me about my previous post on deployment plans that still had questions about a more complex example of using WebLogic Server deployment API support for JSR-88 Deployment Plans.  Steve Button – one of the WebLogic Server Product Managers -  provided a very helpful presentation on Deployment Plans that I will embed below. 

I have also posted a Deployment Plan Sample on http://samplecode.oracle.com.  The sample illustrates how to change a web.xml context parameter value from “development” to “qualityassurance” to “production” all without modifying the EAR file at all.  The presentation and sample should help you become more familiar with the capabilities.

UPDATED 9/9/2009 – Also there is a complete Oracle By Example – showing how to create and use a Deployment Plan step-by-step.

August 25, 2009

BEA Product Documentation is Moving to Oracle.com Websites

As part of the decommissioning of bea.com websites to oracle.com websites, edocs.bea.com is going to be shutdown at the end of August 2009.  This means if you have bookmarked or linked to edocs in other places, those links will break.  Metalink Note 876228.1 has the official announcement on the support website.

The new OTN URL for legacy BEA releases is:
http://www.oracle.com/technology/documentation/bea_doc_index.html
The new OTN URL for legacy WLS releases is:
http://www.oracle.com/technology/documentation/weblogic_server.html

My Documentation Tips

In case you forget or misplace these URL’s it’s helpful to know how to navigate there from scratch.  Let me give you a short tour that will at least point you on how to find the older WebLogic Server release documentation.  The documentation for the 11g release of middleware (powered by WebLogic 10.3.1) has a new look and feel compared to edocs.   You can always find the documentation by starting at OTN http://www.oracle.com/technology/.

 

 

 

Personally the way I like to use the documentation is via google search.

I have a keyboard shortcut that allows me to type “wls securing web applications” which then automatically opens a the google search for:

Check out the results, it works pretty well.

http://www.google.com/search?&ie=utf-8&oe=utf-8&rls=org.mozilla:en-US:official&client=firefox-a&q=site%3Ahttp%3A%2F%2Fdownload.oracle.com%2Fdocs%2Fcd%2FE12839_01%2F+securing+web+applications

July 22, 2009

11gR1 Update and a Deployment Plan Example for WebLogic Server

7-23-09 Update: I updated this post slightly overnight after I thought of a few additional comments, an easier approach and I took in feedback from a comment.

11gR1 Release

It is a very exciting time to be involved with Oracle Fusion Middleware.  First, there are many new features and capabilities in the recently released 11gR1 that I’m learning about in training this week.  My focus is mostly on WebLogic Server and surrounding technologies categorized in the Application Grid space and the SOA products.  Beyond the best-of-breed qualities in the stand-alone middleware products, there is an ever-increasing amount of synergy between Oracle’s other products, both middleware and elsewhere.  The number of products certified and making use of WebLogic Server is incredible.  This has high value for customers in the consolidation of skill-sets and infrastructure.  I am personally very impressed with the amount of innovation that has occurred in a little over a year since Oracle acquired BEA Systems, and I’m excited about what is coming in future releases.  My blog folder where I store ideas for future blog entries just keeps getting more and more backlogged as I come across things worth investigating and sharing.

Deployment Plan JSR-88 Example in WebLogic Server

The Forum Question

One of the ways I gauge the increasing interest in WebLogic Server is the increasing number of posts in the WebLogic Server - General forum on OTN.  Today was an especially busy day and someone inquired on a fairly straight-forward problem:

Can I change the context root of a web application without modifying the archive?

How can you do it?

Fortunately if you need to make minor configuration adjustments in application deployment descriptors (such as web.xml, weblogic.xml, application.xml, or weblogic-application.xml), but do not need to change any application code you can use a Deployment Plan (also known as JSR-88) to do this in WebLogic Server deployments.  I’m not going to go into great detail here, but the essential idea is that an xml file, usually called Plan.xml that is located outside the application archive, can be used to override or set undeclared values in the deployment descriptors that are embedded in an application archive.  See the image below.

Why would you ever need this?

There are many reasons why you may not want to modify an application archive, one of which is testing.  For example, if you have successfully completed testing for a particular version an application, it is desirable to keep the application archive unmodified between environments so you can have increased confidence that the application will behave the same in multiple environments as it is promoted.  Another reason might be portability.  You could have one generic J2EE or JEE application archive without proprietary deployment descriptors and put all of those proprietary deployment descriptor values outside of that archive.  I know that WebLogic Portal code used this feature at one time to change the verbosity of a log level that was set with a Servlet initialization paramter, so you could also use this to change configuration values at runtime provided those are in a supported deployment descriptor.

Several other WebLogic bloggers have addressed Deployment Plans, so before I write about how I addressed this particular forum question, just know that you can get a general overview and more detailed examples from the following posts:

A Simple Example

There is a context-root element in a web application module’s weblogic.xml file that specifies the context root of the application.  When building a web application with Oracle Enterprise Pack for Eclipse, the value is automatically set to the Web Application name.  Check out the WEB-INF/weblogic.xml from my Web Application named PlanWEB.

<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app" 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_2_5.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd">
    <wls:weblogic-version>10.3.1</wls:weblogic-version>
    <wls:context-root>PlanWEB</wls:context-root>
</wls:weblogic-web-app>

I have confirmed that my application is deployed and listening at the /PlanWEB context root on my server.  How you deploy it, weblogic.Deployer, WLST, or the console is up to you.  Now lets say I want to change the context root to /FooWEB at runtime, even though the weblogic.xml file in the application archive will have PlanWEB listed in the deployment descriptor.

Option 1 – The Console – The easy GUI driven way

Browse the deployment, specifically to the web module of the context root you want to change.  Here’s a shot of my Deployments page after I have expanded the EAR to see it’s modules.

Home –> Deployments –> PlanEAR

console1

Click on PlanWEB, then click the configuration tab and look at the bottom of that page.  You’ll see the current context root, but not editable (depending on your console preferences it may already be editable).

contextRoot

  • Lock and edit the console if you have not already and put in a new value like “FooWEB” and click the save button.
  • The next page should prompt you for the location where the console should write the plan.xml file on the file system.  Choose whatever location makes sense, but it probably makes sense to put it somewhere in the domain directory structure or some other standard or convention you come up with.
  • Now activate the session.  The context root should now be /FooWEB and the configuration tab of the console show now also reflect that.

FooWEB

Here is my plan.xml that the WebLogic Server Console generated for me.

<?xml version='1.0' encoding='UTF-8'?>
<deployment-plan xmlns="http://xmlns.oracle.com/weblogic/deployment-plan" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/deployment-plan http://xmlns.oracle.com/weblogic/deployment-plan/1.0/deployment-plan.xsd">
  <application-name>foo</application-name>
  <variable-definition>
    <variable>
      <name>WeblogicWebApp_ContextRoots_12483492503430</name>
      <value>"FooWEB"</value>
    </variable>
  </variable-definition>
  <module-override>
    <module-name>PlanEAR.ear</module-name>
    <module-type>ear</module-type>
    <module-descriptor external="false">
      <root-element>weblogic-application</root-element>
      <uri>META-INF/weblogic-application.xml</uri>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>application</root-element>
      <uri>META-INF/application.xml</uri>
    </module-descriptor>
    <module-descriptor external="true">
      <root-element>wldf-resource</root-element>
      <uri>META-INF/weblogic-diagnostics.xml</uri>
    </module-descriptor>
  </module-override>
  <module-override>
    <module-name>PlanWEB.war</module-name>
    <module-type>war</module-type>
    <module-descriptor external="false">
      <root-element>weblogic-web-app</root-element>
      <uri>WEB-INF/weblogic.xml</uri>
      <variable-assignment>
        <name>WeblogicWebApp_ContextRoots_12483492503430</name>
        <xpath>/weblogic-web-app/context-root</xpath>
        <operation>replace</operation>
      </variable-assignment>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>web-app</root-element>
      <uri>WEB-INF/web.xml</uri>
    </module-descriptor>
  </module-override>
  <config-root>C:\temp\foo\plan</config-root>
</deployment-plan>

Option 2 – weblogic.PlanGenerator – a more manual approach that could be used for automation

Here are the steps for creating the deployment plan yourself with weblogic.PlanGenerator.

  • Export the EAR file to a directory – my enterprise archive name is PlanEAR.ear
  • Go to your domain's bin dir with a shell and call setDomainEnv
  • Call a command like this from the dir where the EAR is with the configured shell:

java weblogic.PlanGenerator -all -plan Plan.xml PlanEAR.ear
  • That should generate a Plan.xml file in that directory with a bunch of stubs for values you could change at runtime in the Plan.xml file.  
  • Change the value of the variable for the context root.  Note that the variable name is auto-generated, but it still yields a clue by the variable name prefix.

Original:

<variable>
<name>WeblogicWebApp_ContextRoots_12482790756401</name>
<value xsi:nil="true"></value>
</variable>

New:

<variable>
<name>WeblogicWebApp_ContextRoots_12482790756401</name>
<value>FooWEB</value>
</variable>
  • Note that I did not use quotes for FooWEB whereas the console approach did use them.  Either way seemed to work just fine.
  • In order to override the context root we need to do a replace operation in the variable assignment since the element already exists in the archive. Edit Plan.xml again, this time modifying the relevant variable assignment section of the Plan.xml.  Note, if the weblogic.xml did not already have the context-root explicitly listed as an xml element, then the original section could have been used.

Original:

<variable-assignment> 
<name>WeblogicWebApp_ContextRoots_12482790756401</name> 
<xpath>/weblogic-web-app/context-root</xpath>
</variable-assignment>

New:

<variable-assignment> 
<name>WeblogicWebApp_ContextRoots_12482790756401</name> 
<xpath>/weblogic-web-app/context-root</xpath>
<operation>replace</operation> 
</variable-assignment>

Now we can use the weblogic.Deployer command for a redeployment with this new deployment plan Plan.xml and the EAR.  We also could have used WLST.

java weblogic.Deployer -adminurl t3://localhost:7001 -user weblogic -password welcome1 -redeploy -name PlanEAR -source PlanEAR.ear -targets AdminServer -plan Plan.xml
After the command completes, I can now access the application at http://locahost:7001/FooWEB/.


Special thanks to fellow bloggers Max, Edwin, and Benoit for their entries related to this topic.  Cheers.

April 9, 2009

Using WLST with the WebLogic Server Job Scheduler

I recently wrote about a simple example using the WebLogic Server Job Scheduler.  This describes how to cancel the jobs and get visibility into job execution status.

jmx [2]

Cancelling a task

One of the things to note about the JobScheduler is that you cannot call certain CommonJ API calls, particularly around cancelling tasks.  The way to do that is via the JobSchedulerRuntimeMBean, exposed with console functionality or via JMX.  Obviously the console is great for on-demand access, but what if you want to script things to make automated decisions?

Here is some sample output from a WLST session that shows me browsing to the JobSchedulerRuntimeMBean, finding an active task and cancelling it.

wls:/offline> connect('weblogic','weblogic','t3://localhost:7011')
Connecting to t3://localhost:7011 with userid weblogic ...
Successfully connected to managed Server 'managedServer1' that belongs to domain 'wldf_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.
wls:/wldf_domain/serverConfig> domainRuntime()
DomainRuntime MBeanServer is not enabled on a Managed Server.
wls:/wldf_domain/serverConfig> ls()
dr--   AdminConsole
dr--   AppDeployments
dr--   BridgeDestinations
dr--   Clusters
dr--   CustomResources
dr--   DeploymentConfiguration
dr--   Deployments
dr--   EmbeddedLDAP
dr--   ErrorHandlings
dr--   FileStores
dr--   InternalAppDeployments
dr--   InternalLibraries
dr--   JDBCDataSourceFactories
dr--   JDBCStores
dr--   JDBCSystemResources
dr--   JMSBridgeDestinations
dr--   JMSInteropModules
dr--   JMSServers
dr--   JMSSystemResources
dr--   JMX
dr--   JTA
dr--   JoltConnectionPools
dr--   Libraries
dr--   Log
dr--   LogFilters
dr--   Machines
dr--   MailSessions
dr--   MessagingBridges
dr--   MigratableTargets
dr--   RemoteSAFContexts
dr--   SAFAgents
dr--   SNMPAgent
dr--   SNMPAgentDeployments
dr--   Security
dr--   SecurityConfiguration
dr--   SelfTuning
dr--   Servers
dr--   ShutdownClasses
dr--   SingletonServices
dr--   StartupClasses
dr--   SystemResources
dr--   Targets
dr--   VirtualHosts
dr--   WLDFSystemResources
dr--   WLECConnectionPools
dr--   WSReliableDeliveryPolicies
dr--   WTCServers
dr--   WebAppContainer
dr--   WebserviceSecurities
dr--   XMLEntityCaches
dr--   XMLRegistries
-r--   AdminServerName                              AdminServer
-r--   AdministrationMBeanAuditingEnabled           false
-r--   AdministrationPort                           9002
-r--   AdministrationPortEnabled                    false
-r--   AdministrationProtocol                       t3s
-r--   ArchiveConfigurationCount                    0
-r--   ClusterConstraintsEnabled                    false
-r--   ConfigBackupEnabled                          false
-r--   ConfigurationAuditType                       none
-r--   ConfigurationVersion                         10.3.0.0
-r--   ConsoleContextPath                           console
-r--   ConsoleEnabled                               true
-r--   ConsoleExtensionDirectory                    console-ext
-r--   DomainVersion                                10.3.0.0
-r--   GuardianEnabled                              false
-r--   InternalAppsDeployOnDemandEnabled            true
-r--   LastModificationTime                         0
-r--   Name                                         wldf_domain
-r--   Notes                                        null
-r--   Parent                                       null
-r--   ProductionModeEnabled                        false
-r--   RootDirectory                                C:\Oracle\wls10gR3\user_projects\domains\wldf_domain
-r--   Type                                         Domain
-r-x   freezeCurrentValue                           Void : String(attributeName)
-r-x   isSet                                        Boolean : String(propertyName)
-r-x   unSet                                        Void : String(propertyName)
wls:/wldf_domain/serverConfig> serverRuntime()
wls:/wldf_domain/serverRuntime> ls()
dr--   ApplicationRuntimes
dr--   AsyncReplicationRuntime
dr--   ClusterRuntime
dr--   ConnectorServiceRuntime
dr--   DefaultExecuteQueueRuntime
dr--   EntityCacheCumulativeRuntime
dr--   EntityCacheCurrentStateRuntime
dr--   EntityCacheHistoricalRuntime
dr--   ExecuteQueueRuntimes
dr--   JDBCServiceRuntime
dr--   JMSRuntime
dr--   JTARuntime
dr--   JVMRuntime
dr--   JoltRuntime
dr--   LibraryRuntimes
dr--   LogBroadcasterRuntime
dr--   LogRuntime
dr--   MANAsyncReplicationRuntime
dr--   MANReplicationRuntime
dr--   MailSessionRuntimes
dr--   MaxThreadsConstraintRuntimes
dr--   MinThreadsConstraintRuntimes
dr--   PathServiceRuntime
dr--   PersistentStoreRuntimes
dr--   RequestClassRuntimes
dr--   SAFRuntime
dr--   SNMPAgentRuntime
dr--   ServerChannelRuntimes
dr--   ServerSecurityRuntime
dr--   ServerServices
dr--   SingleSignOnServicesRuntime
dr--   ThreadPoolRuntime
dr--   TimerRuntime
dr--   WANReplicationRuntime
dr--   WLDFRuntime
dr--   WTCRuntime
dr--   WebServerRuntimes
dr--   WorkManagerRuntimes
-r--   ActivationTime                               1237559025844
-r--   AdminServer                                  false
-r--   AdminServerHost                              192.168.2.1
-r--   AdminServerListenPort                        7001
-r--   AdminServerListenPortSecure                  false
-r--   AdministrationPort                           9002
-r--   AdministrationPortEnabled                    false
-r--   AdministrationURL                            t3://192.168.2.1:7011
-r--   CurrentDirectory                             C:\Oracle\wls10gR3\user_projects\domains\wldf_domain\.
-r--   CurrentMachine
-r--   DefaultExecuteQueueRuntime                   null
-r--   DefaultURL                                   t3://192.168.2.1:7011
-r--   EntityCacheCumulativeRuntime                 null
-r--   EntityCacheCurrentStateRuntime               null
-r--   EntityCacheHistoricalRuntime                 null
-r--   HealthState                                  Component:ServerRuntime,State:HEALTH_OK,MBean:managedServer1,ReasonCode:[]
-r--   JoltRuntime                                  null
-r--   ListenAddress                                JBAYER-LAP/192.168.2.1
-r--   ListenPort                                   7011
-r--   ListenPortEnabled                            true
-r--   MANAsyncReplicationRuntime                   null
-r--   MANReplicationRuntime                        null
-r--   Name                                         managedServer1
-r--   OpenSocketsCurrentCount                      3
-r--   Parent                                       null
-r--   PathServiceRuntime                           null
-r--   RestartRequired                              false
-r--   RestartsTotalCount                           0
-r--   SSLListenAddress                             null
-r--   SSLListenPort                                7002
-r--   SSLListenPortEnabled                         false
-r--   ServerClasspath                              C:\Oracle\wls10gR3\JROCKI~1\jre\bin\jrockit\jrockit1.6.0.jar;C:\Oracle\wls10gR3\JROCKI~1\jre\bin\jrockit\jmapi.jar;C:\Oracle\wls10gR3\JROCKI~1\
3\JROCKI~1\jre\bin\jrockit\rmp.jar;C:\Oracle\wls10gR3\JROCKI~1\jre\bin\jrockit\latency.jar;C:\Oracle\wls10gR3\JROCKI~1\jre\lib\resources.jar;C:\Oracle\wls10gR3\JROCKI~1\jre\lib\rt.jar;C:\Oracle\w
acle\wls10gR3\JROCKI~1\jre\lib\jsse.jar;C:\Oracle\wls10gR3\JROCKI~1\jre\lib\jce.jar;C:\Oracle\wls10gR3\JROCKI~1\jre\lib\charsets.jar;C:\Oracle\wls10gR3\JROCKI~1\jre\classes;C:\JDeveloper\mywork\J
ener.jar;C:\Oracle\wls10gR3\patch_wlw1030\profiles\default\sys_manifest_classpath\weblogic_patch.jar;C:\Oracle\wls10gR3\patch_wls1030\profiles\default\sys_manifest_classpath\weblogic_patch.jar;C:
ult\sys_manifest_classpath\weblogic_patch.jar;C:\Oracle\wls10gR3\patch_cie670\profiles\default\sys_manifest_classpath\weblogic_patch.jar;C:\Oracle\wls10gR3\patch_cie660\profiles\default\sys_manif
s10gR3\patch_alsb1030\profiles\default\sys_manifest_classpath\weblogic_patch.jar;C:\Oracle\wls10gR3\JROCKI~1\lib\tools.jar;C:\Oracle\wls10gR3\WLSERV~1.3\server\lib\weblogic_sp.jar;C:\Oracle\wls10
cle\wls10gR3\modules\features\weblogic.server.modules_10.3.0.0.jar;C:\Oracle\wls10gR3\WLSERV~1.3\server\lib\webservices.jar;C:\Oracle\wls10gR3\modules\ORGAPA~1.5/lib/ant-all.jar;C:\Oracle\wls10gR
\Oracle\wls10gR3\jdeveloper\modules\features\adf.share_11.1.1.jar;;C:\Oracle\wls10gR3\WLSERV~1.3\common\eval\pointbase\lib\pbclient57.jar;C:\Oracle\wls10gR3\WLSERV~1.3\server\lib\xqrl.jar;;;C:\Or
sysext_manifest_classpath\weblogic_ext_patch.jar
-r--   SocketsOpenedTotalCount                      3
-r--   State                                        RUNNING
-r--   StateVal                                     2
-r--   Type                                         ServerRuntime
-r--   WANReplicationRuntime                        null
-r--   WLECConnectionServiceRuntime                 null
-r--   WeblogicVersion                              WebLogic Server Temporary Patch for CR380042 Thu Sep 11 13:33:40 PDT 2008
WebLogic Server Temporary Patch for 7372756 Fri Sep 12 17:05:44 EDT 2008
WebLogic Server Temporary Patch for CR380913 Wed Oct 15 13:24:22 PDT 2008
WebLogic Server Temporary Patch for CR381739 Tue Oct 21 14:06:14 IST 2008
WebLogic Server Temporary Patch for CR381056 Mon Oct 06 10:48:50 EDT 2008
WebLogic Server Temporary Patch for CR374413, CR378680 Tue Sep 02 09:55:36 PDT 2008
WebLogic Server Temporary Patch for CR378102 Wed Sep 10 23:28:48 PDT 2008
WebLogic Server Temporary Patch for CR378741 Tue Sep 09 13:08:51 PDT 2008
WebLogic Server Temporary Patch for CR378781, CR380313 Fri Sep 19 13:34:16 PDT 2008
WebLogic Server 10.3  Fri Jul 25 16:30:05 EDT 2008 1137967
-r-x   addRequestClassRuntime                       Boolean : WebLogicMBean(weblogic.management.runtime.RequestClassRuntimeMBean)
-r-x   forceShutdown                                Void :
-r-x   forceSuspend                                 Void :
-r-x   getServerChannel                             java.net.InetSocketAddress : String(protocol)
-r-x   getURL                                       String : String(protocol)
-r-x   preDeregister                                Void :
-r-x   restartSSLChannels                           Void :
-r-x   resume                                       Void :
-r-x   shutdown                                     Void :
-r-x   shutdown                                     Void : Integer(timeout),Boolean(ignoreSessions)
-r-x   start                                        Void :
-r-x   suspend                                      Void :
-r-x   suspend                                      Void : Integer(timeout),Boolean(ignoreSessions)
wls:/wldf_domain/serverRuntime> cd('ClusterRuntime')
wls:/wldf_domain/serverRuntime/ClusterRuntime> ls()
dr--   myCluster
wls:/wldf_domain/serverRuntime/ClusterRuntime> cd('myCluster')
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster> ls()
dr--   JobSchedulerRuntime
dr--   ServerMigrationRuntime
dr--   UnicastMessaging
-r--   ActiveSingletonServices                      java.lang.String[TimerMaster]
-r--   AliveServerCount                             1
-r--   CurrentMachine                               null
-r--   CurrentSecondaryServer
-r--   DetailedSecondariesDistribution              null
-r--   ForeignFragmentsDroppedCount                 0
-r--   FragmentsReceivedCount                       581
-r--   FragmentsSentCount                           581
-r--   HealthState                                  Component:null,State:HEALTH_OK,MBean:null,ReasonCode:[]
-r--   MulticastMessagesLostCount                   0
-r--   Name                                         myCluster
-r--   PrimaryCount                                 0
-r--   ResendRequestsCount                          0
-r--   SecondaryCount                               0
-r--   SecondaryDistributionNames                   null
-r--   SecondaryServerDetails
-r--   ServerNames                                  java.lang.String[managedServer1]
-r--   Type                                         ClusterRuntime
-r--   UnicastMessaging                             null
-r-x   preDeregister                                Void :
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster> cd('JobSchedulerRuntime')
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster/JobSchedulerRuntime> ls()
dr--   JobSchedulerRuntime
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster/JobSchedulerRuntime> cd('JobSchedulerRuntime')
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster/JobSchedulerRuntime/JobSchedulerRuntime> ls()
dr--   ExecutedJobs
-r--   Name                                         JobSchedulerRuntime
-r--   Type                                         JobSchedulerRuntime
-r-x   getJob                                       WebLogicMBean : String(id)
-r-x   preDeregister                                Void :
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster/JobSchedulerRuntime/JobSchedulerRuntime> cd('ExecutedJobs')
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster/JobSchedulerRuntime/JobSchedulerRuntime/ExecutedJobs> ls()
dr--   JobRuntime-managedServer1_1237564053328
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster/JobSchedulerRuntime/JobSchedulerRuntime/ExecutedJobs> cd('JobRuntime-managedServer1_1237564053328')
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster/JobSchedulerRuntime/JobSchedulerRuntime/ExecutedJobs/JobRuntime-managedServer1_1237564053328> ls()
-r--   Description                                  jamesbayer.client.SystemOutTimerListener@2ed941
-r--   ID                                           managedServer1_1237564053328
-r--   LastLocalExecutionTime                       1237564580328
-r--   LocalExecutionCount                          18
-r--   Name                                         JobRuntime-managedServer1_1237564053328
-r--   Period                                       30000
-r--   State                                        Running
-r--   Timeout                                      6719
-r--   TimerListener                                jamesbayer.client.SystemOutTimerListener@333c5d
-r--   Type                                         JobRuntime
-r-x   cancel                                       Void :
-r-x   preDeregister                                Void :
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster/JobSchedulerRuntime/JobSchedulerRuntime/ExecutedJobs/JobRuntime-managedServer1_1237564053328> ls()
-r--   Description                                  jamesbayer.client.SystemOutTimerListener@2ed941
-r--   ID                                           managedServer1_1237564053328
-r--   LastLocalExecutionTime                       1237564850547
-r--   LocalExecutionCount                          27
-r--   Name                                         JobRuntime-managedServer1_1237564053328
-r--   Period                                       30000
-r--   State                                        Running
-r--   Timeout                                      18812
-r--   TimerListener                                jamesbayer.client.SystemOutTimerListener@349b7f
-r--   Type                                         JobRuntime
-r-x   cancel                                       Void :
-r-x   preDeregister                                Void :
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster/JobSchedulerRuntime/JobSchedulerRuntime/ExecutedJobs/JobRuntime-managedServer1_1237564053328> cmo.cancel()
wls:/wldf_domain/serverRuntime/ClusterRuntime/myCluster/JobSchedulerRuntime/JobSchedulerRuntime/ExecutedJobs/JobRuntime-managedServer1_1237564053328> ls()
-r--   Description                                  jamesbayer.client.SystemOutTimerListener@2ed941
-r--   ID                                           managedServer1_1237564053328
-r--   LastLocalExecutionTime                       1237564850547
-r--   LocalExecutionCount                          27
-r--   Name                                         JobRuntime-managedServer1_1237564053328
-r--   Period                                       30000
-r--   State                                        Cancelled
-r--   Timeout                                      -1
-r--   TimerListener                                null
-r--   Type                                         JobRuntime
-r-x   cancel                                       Void :
-r-x   preDeregister                                Void :

Automating JobScheduler status checking

One of my customers asked me about how they could periodically check if a task was running in the cluster, and if it is not then send someone an email, etc.  Basically, the sky is the limit with scripting.  A more advanced use of this would be to connect this to alerts in WebLogic Diagnsotic Framework or SNMP traps in Oracle Enterprise Manager, but this is a start.  Here is a python script that you can invoke with WLST that will do several things:

  • Connect to the Admin Server
  • Get a list of all managed servers in the cluster
  • Check each managed server for a particular running timer task
  • If no running tasks are found exit with a non-zero exit code, otherwise exit a zero exit code

isJobRunning.py

user='weblogic'
pw='weblogic'
adminServer='localhost'
adminPort='7001'
clusterName='myCluster'
timerRunning=false
timerClass='jamesbayer.client.SystemOutTimerListener'
## Standard Connection
try:
  connect(user,pw,'t3://'+adminServer+':'+adminPort)
except:
  print 'Error connecting to Admin Server'
## Get the list of managed servers in the cluster
try:
  domainConfig()
  cd('Clusters/'+clusterName)
  managedServers=cmo.getServers()
  print 'Found ' + `len(managedServers)` + ' managed servers'
except:
  print 'Error navigating DomainConfig MBean tree for the list of managed servers'
## Loop through managed servers and look for the job
domainRuntime()
for managedServer in managedServers:
  try:
    managedServerName=managedServer.getName()
    print 'Trying ' + managedServerName
    cd('/ServerRuntimes/'+managedServerName)
    if cmo.getState() == 'RUNNING':
      cd( '/ServerRuntimes/'+managedServerName+'/ClusterRuntime/'+clusterName+'/JobSchedulerRuntime/JobSchedulerRuntime')
      try:
        jobs=cmo.getExecutedJobs()
        for job in jobs:
          if job.getDescription().find(timerClass) > -1 and job.getState() == 'Running':
            timerRunning=true
            print timerClass + ' is running on ' +  managedServerName
      except:
        print 'Error checking ' + managedServerName + ' ExecutedJobs'
    else:
      print managedServerName + ' is not RUNNING'
  except:
    print 'Error checking JobScheduler on ' + managedServerName
  if timerRunning:
    break
disconnect()
if timerRunning:
  print 'The Job is Running'
  exit(exitcode=0)
else:
  print 'The Job is NOT Running'
  exit(exitcode=1)

Script output

C:\Oracle\wls10gR3\user_projects\domains\wldf_domain>bin\setDomainEnv.cmd
C:\Oracle\wls10gR3\user_projects\domains\wldf_domain>java weblogic.WLST C:\MyData\Accounts\PepsiAmericas\JobScheduler\isJobRunning.py
Initializing WebLogic Scripting Tool (WLST) ...
Welcome to WebLogic Server Administration Scripting Shell
Type help() for help on available commands
Connecting to t3://localhost:7001 with userid weblogic ...
Successfully connected to Admin Server 'AdminServer' that belongs to domain 'wldf_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.
Location changed to serverRuntime tree. This is a read-only tree with DomainMBean as the root.
For more help, use help(domainConfig)
Found 1 managed servers
Location changed to domainRuntime tree. This is a read-only tree with DomainMBean as the root.
For more help, use help(domainRuntime)
Trying managedServer1
Disconnected from weblogic server: AdminServer
The Job is Running
Exiting WebLogic Scripting Tool.
C:\Oracle\wls10gR3\user_projects\domains\wldf_domain>

April 8, 2009

A Simple Job Scheduler Example - WebLogic Server Clustered Timer

Did you know WebLogic Server can schedule future tasks similar to a cron job?  This even works in a WebLogic cluster.  Using a cluster ensures that as long as one of the managed servers in the cluster is available, your task has a high-availability characteristic and the task will execute.  This has come up for at least 3 of my local customers.  Every once in awhile I see questions about WebLogic timers from my peers, so I thought I’d share a simple example for how this works and some extra tidbits based on personal experience that might not be in the standard documentation.  Read on if you want to see how to schedule a simple println() task to execute every 30 seconds in a cluster that can survive server failures in both the WebLogic tier and the database tier (provided you use Oracle RAC).

HA

Background

Oracle (then BEA Systems) and IBM collaborated on a common API for Timers and Work Managers (think multi-threading) that would be portable across application servers.  They aptly named this API CommonJ.  This API has been supported in WebLogic Server since 9.x and apparently in WebSphere since 6.x.  When combined with WebLogic clustering support, the CommonJ Timer API is known as the Job Scheduler.  It requires some configuration as described in the docs, but essentially the way it works under the covers is by serializing the class that will execute the task and some related metadata to a database.  Using the leasing feature of WebLogic Server, exactly one cluster member will be responsible for JobScheduler (singleton service) which executes the Timer Tasks.  The server responsible for this service will check the database every 30 seconds to see if there are new tasks to execute.

Instructions for the System Out Example Timer

These instructions assume starting from scratch with nothing but a basic WebLogic Server installation and a database like Oracle XE.  I run all of this easily on my 2+ yr old laptop (I do have 3+ gig of ram).  The documentation is more detailed now, so definitely refer to that as well.

  1. Create a new domain with 1 Admin Server and at least one Managed Server that will be a part of a cluster.  Refer to the Oracle By Example page for WLS if you need help with this, but you should be able to do all of this from the Configuration Wizard.  I named my cluster “myCluster” and my managed servers “managedServer1” and “managedServer2”.
  2. Set up the database – Basically run the DDL for your DB, which will create a table called WEBLOGIC_TIMERS. If you don’t set up database often, here is my simple POC sequence with Oracle XE.  In production, I would recommend Oracle RAC with a Multi Data Source, but you can POC this with an XE database or a single database node if you can live with a single point of failure.  Open the XE database web console and create a new database user, of course I use weblogic/weblogic for the user name password and give them admin rights.  Start sqlplus and login as that user.  Navigate to the directory that contains the DDL you want to run and use the @ syntax to execute the DDL like this.  You can of course ignore the messages about the comments not being valid commands: scheduler.ddl
  3. Keep the sql prompt open, we’ll use it again in a minute.  Notice that the script is in the oracle\920 directory, that simply means for 9.2+ of Oracle DB, it works totally fine for me on XE 10g.
  4. Configure the data source – Create a new data source for this and target it at all the servers in your domain (or at least the ones in your cluster) and specify it as the cluster’s JobScheduler Data Source.  I recommend that you test your data source when you are creating it.
  5. Set up leasing – You can use consensus or database leasing, but since we already using the DB for the Job Scheduler, just do database leasing which creates a table called ACTIVE (at least in the recent releases of WLS, it used to be something like WEBLOGIC_ACTIVE in earlier releases).  This means running the appropriate WL_HOME/server/db/dbname/leasing.ddl as well as configuring the data source, which can be the same one you used before, and configuring the cluster to use that data source for leasing.  Make sure you note database based leasing requires a highly-available database.  If the connection goes away, the managed servers will fail.  For this reason you can use multi data sources with Oracle RAC to get an even more HA architecture, but Oracle XE works fine for a POC.  I got consensus leasing to work at one time in the past using WLS 9.2, but if I remember correctly I had to install Node Manager and configure “machines” for my servers for it to work.  So DB-based leasing is what I’m using in this example because it’s simpler and I have not had issues with it before. leasing.ddl
  6. Write some code – There is a sample in the docs showing how to look up the job scheduler and schedule a task.  I’ll take care of that for you, here is a WAR (Servlet 2.5 based) that has code to register the task via a servlet and a JAR file that contains the class that implements the TimerListener interface.  The source code is at the way bottom of this post.
  7. Deploy the WAR file, target it at the cluster, and make sure the application is started.
  8. Edit your server classpath to contain the timer implementation class which is in the JAR referenced earlier.  You might be asking yourself, why the heck does the timer listener class need to be placed on the system classpath?!!  Well if you think about how the classloaders work, the singleton service running the tasks does not have access to the classes from the JEE module that registered the timer.  It is running as a WebLogic system thread.  This class will be deserialized from the database, potentially on another server.  So I recommend not doing anything too sophisticated in the timer listener.  If you have lots of logic with multiple classes, just consider having the timer listener drop a message on a distributed JMS queue, which can have an MDB from another application that does all of your heavy lifting.  Back to putting the TimerListener implementation on the classpath, I accomplished this by added the following line to my domain’s setDomainEnv.cmd script.  There is also the option to use an EJB Timer, which apparently does not have the system classpath requirement, but I haven’t tried that before.
    set PRE_CLASSPATH=C:\JDeveloper\mywork\JobSchedulerApp\TimerListener\deploy\TimerListener.jar
    
  9. Optional – Enable verbose logging for the related sub-systems, I find this helpful when troubleshooting. 
    set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.debug.DebugSingletonServices=true -Dweblogic.JobScheduler=true

  10. Restart your Admin Server and Managed Server.  Make sure you see the JAR in the classpath settings in the log file.  args
  11. If your enabled the additional logging, you should see that leasing and job scheduler are creating log entries like the following in the managed server log file (not in standard out!). 
  12. ####<Apr 8, 2009 3:10:01 PM CDT> <Debug> <SingletonServices> <jbayer-lap> <managedServer1> <[ACTIVE] ExecuteThread: '1' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1239221401078> <BEA-000000> <SingletonMonitor: Checking Failed Leases> 
    ####<Apr 8, 2009 3:10:11 PM CDT> <Debug> <SingletonServices> <jbayer-lap> <managedServer1> <[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1239221411078> <BEA-000000> <SingletonMonitor: Now checking lease statuses.> 
    ####<Apr 8, 2009 3:10:11 PM CDT> <Debug> <SingletonServices> <jbayer-lap> <managedServer1> <[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1239221411078> <BEA-000000> <SingletonMonitor: Checking for registered Migratable Targets and Singleton Services without a lease> 
    ####<Apr 8, 2009 3:10:11 PM CDT> <Debug> <SingletonServices> <jbayer-lap> <managedServer1> <[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1239221411078> <BEA-000000> <SingletonMonitor: TimerMaster - Found an owner - -7090657857495109585/managedServer1> 
    ####<Apr 8, 2009 3:10:11 PM CDT> <Debug> <SingletonServices> <jbayer-lap> <managedServer1> <[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1239221411078> <BEA-000000> <SingletonMonitor: Checking existant, but expired leases> 
    ####<Apr 8, 2009 3:10:11 PM CDT> <Debug> <SingletonServices> <jbayer-lap> <managedServer1> <[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1239221411078> <BEA-000000> <SingletonMonitor: Checking Failed Leases> 
    ####<Apr 8, 2009 3:10:21 PM CDT> <Debug> <SingletonServices> <jbayer-lap> <managedServer1> <[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1239221421078> <BEA-000000> <SingletonMonitor: Now checking lease statuses.> 
    ####<Apr 8, 2009 3:10:21 PM CDT> <Debug> <SingletonServices> <jbayer-lap> <managedServer1> <[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1239221421078> <BEA-000000> <SingletonMonitor: Checking for registered Migratable Targets and Singleton Services without a lease> 
    ####<Apr 8, 2009 3:10:21 PM CDT> <Debug> <SingletonServices> <jbayer-lap> <managedServer1> <[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1239221421078> <BEA-000000> <SingletonMonitor: TimerMaster - Found an owner - -7090657857495109585/managedServer1> 
    ####<Apr 8, 2009 3:10:21 PM CDT> <Debug> <SingletonServices> <jbayer-lap> <managedServer1> <[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1239221421078> <BEA-000000> <SingletonMonitor: Checking existant, but expired leases> 
    ####<Apr 8, 2009 3:10:21 PM CDT> <Debug> <SingletonServices> <jbayer-lap> <managedServer1> <[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1239221421078> <BEA-000000> <SingletonMonitor: Checking Failed Leases> 

  13. Register the timer by hitting the URL or invoking the web service of one of the managed servers.  In my case that is http://localhost:7011/TimerWeb/timerservlet.  Note that each time you invoke the URL successfully a new timer listener will be registered with the cluster, so you probably only want to do this once.
  14. Notice the messages in standard out if this is working correctly.
  15. timerExpired() called at 4/8/09 3:12 PM
    timerExpired() called at 4/8/09 3:13 PM

  16. Shut down the managed server that has the messages printing to standard out.  You should see the messages show up on another managed server in the cluster in standard out.
  17. Now shut down all of the managed servers and restart at least one.
  18. You should see the timer task start printing to standard out when one of the managed servers in the cluster is available.

Summary

So you should have a working timer example in WebLogic Server that survives failures.  The debug messages in the managed server log files can be helpful in trouble-shooting in case anything goes wrong.  A couple other things to note, you cannot cancel timer tasks via CommonJ API calls when running in the JobScheduler.  You have to use the JobRuntimeMBean for that.  I’ll show an example of using this bean with WLST in another entry.  Here is the source code for the servlet and the timer listener classes.  Hopefully this will help you utilize this powerful capability built-in to WebLogic.

package view.jamesbayer;
import commonj.timers.TimerManager;
import jamesbayer.client.SystemOutTimerListener;
import java.io.IOException;
import java.io.PrintWriter;
import javax.naming.InitialContext;
import javax.servlet.*;
import javax.servlet.http.*;
public class TimerServlet extends HttpServlet {
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }
    public void service(HttpServletRequest request,
                        HttpServletResponse response) throws ServletException,
                                                             IOException {
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head><title>TimerServlet</title></head>");
        try {
            System.out.println("service() entering try block to intialize the timer from JNDI");
            InitialContext ic = new InitialContext();
            TimerManager jobScheduler =
                (TimerManager)ic.lookup("weblogic.JobScheduler");
            System.out.println("jobScheduler reference " + jobScheduler);
            commonj.timers.TimerListener timerListener =
                new SystemOutTimerListener();
            System.out.println("timerListener reference " + timerListener);
            jobScheduler.schedule(timerListener, 0, 30 * 1000);
            //execute this job every 30 seconds
            System.out.println("service() started the timer");
            out.write("Started the timer - status:");
        } catch (Throwable t) {
            System.out.println("service() error initializing timer manager with JNDI name weblogic.JobScheduler " +                               t);
        }
        out.println("</body></html>");
        out.close();
    }
}

package jamesbayer.client;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import commonj.timers.Timer;
import commonj.timers.TimerListener;
public class SystemOutTimerListener implements Serializable, TimerListener 
{
	private static final long serialVersionUID = 8313912206357147939L;
	public void timerExpired(Timer timer) 
	{
		SimpleDateFormat sdf = new SimpleDateFormat();
		System.out.println( "timerExpired() called at " + sdf.format( new Date() ) );
	}
}

March 25, 2009

Oracle Enterprise Pack for Eclipse 11g – WebLogic Interop Tip

OEPE 11g is out I had to rush out and try it.  I used the full install from OTN.  I assume most users of OEPE 11g will also be WebLogic users.  Out of the box, Workspaces are not pre-configured with a WTP Server Runtime or WebLogic Shared Libraries because OEPE doesn’t know where WebLogic is installed or which version you want to use if you have multiple installations.  There is a simple way to enable this by specifying your WebLogic home in the eclipse.ini file.  I added the following line (substitute your path to WLS):

-Dweblogic.home=C:/Oracle/wls10gR3/wlserver_10.3

Now your Workspaces (existing and new) should become populated with a WebLogic Server Runtime of the version you specified with WebLogic Shared Libraries.  Ah, but there is another gotcha!  In order for this to work with WLS 10gR3 Eclipse needs to be started with a JDK6 JVM.  In my case,  my system’s default JVM was JDK5, so I had to explicitly tell OEPE 11g to start with JDK 6 by specifying the JVM in eclipse.ini.  If you use JRockit instead of Sun you may want to make other adjustments such as making the min/max memory the same and not configuring PermSize at all, but those are just extra tweaks. Again, specify your specific path, but here is mine:

-vm
C:/Oracle/wls10gR3/jrockit_160_05/jre/bin/javaw.exe

Using Windows->Preferences you should be able to see the end result when you launch Eclipse.  Be sure to check out the OEPE 11g tutorials for some great examples.

runtime

libs

March 24, 2009

Eclipse2JDev Series – An Eclipse user tries JDeveloper

Since I’ve joined Oracle via the BEA acquisition I have had the opportunity to start learning JDevleoper and I like it a lot, but I’ve had some bumps in the road since I’m so used to Eclipse.  Much of the IDE tooling for the Oracle’s SOA, BPM, and Application Server products is migrating to JDeveloper, which is Oracle’s comprehensive IDE.  This gives developers using Oracle products a very easy-to-use and highly integrated development environment.  Oracle is still strongly committed to the Eclipse community as evidenced by the contributions to many Eclipse detailed on the Oracle Enterprise Pack for Eclipse page and just announced 11g.  There are some differences for accomplishing certain tasks in JDeveloper and Eclipse and I thought I’d write about my transition from the perspective of a long-time Eclipse user.  Some members of the JDeveloper team have agreed to vet these entries and I hope others will contribute some knowledge as well.  I’ll use Workshop 10gR3 and JDeveloper 11g as my points of comparison.

Application and Project Archive Packaging

This use case was part of the inspiration for this series, simply to have an Enterprise Application with a Web Module that has a dependency on a Java project that is also included in the exported EAR.  In Eclipse WTP, this is accomplished by creating 3 separate projects and linking them together:

  1. Enterprise Application Project – named LibraryTestApp
  2. Utility Project – named LibraryUtility
  3. Dynamic Web Project – named LibraryWeb

As you can see in the J2EE Module Dependencies section of the LibraryTestApp project, I have placed dependencies on the other 2 projects, which results in the LibraryUtility.jar being placed in the EAR’s APP-INF/lib and the WAR being included in the EAR as well.

EclipseDependencies 

EclipseEAR

JDeveloper has the concept of an Application, which is a container for Projects.  So first create a new Generic Application named LibraryTestApp.  The wizard guides you through the creation of a project for this application, and selected JSP and Servlet technologies and name the project LibraryWeb.  Then add a new Java Project to this Application and call it LibraryUtility.  Both of these projects are initially empty, so create a new JSP file in the Web project.  Looking at the structure for the web module, you can see that it has a WEB-INF directory, but no lib directory underneath it.

JDev

How To Create a Directory

I’m going to side-track this a bit and talk about a variation to this use case.  The first time I tried this I already had a pre-built jar file on the file system and my very first intuition was to create a lib directory underneath of WEB-INF and copy the file over.  JDeveloper does not allow you to create folders with a right-click on WEB-INF.  In fact, creating a folder is not directly supported in JDeveloper.  You can indirectly accomplish it by launching the “New…” wizard and generic file, and put the folder name in the Wizard. 

file

lib

This was not exactly intuitive to me as I was used to the Resource Perspective in Eclipse that gives you a pretty good idea of what all the files look like on the file system and lets you manipulate them.  I heard from the product team that this is an often requested feature and is being considered for future JDeveloper releases.  It turns out that you can also just add something to the file system and JDeveloper will pick it up, but there is a better way.  More on that later, but first a little more background on the Application and Project settings.

Deployment Profiles and Dependencies in JDeveloper

The concept of Applications and Projects will be new to Eclipse users where everything is a Project, but this helps group projects together instead of having a cluttered workspace full of potentially unrelated projects.  It turns out that you can right-click on the Application name in the Application Navigator and select Application Properties.  Similarly, you can right click on the Project name and select Project Properties.  Alt-Enter also works as a short-cut exactly as in Eclipse.  Inside of Project Properties there is a menu option for Deployments.  From an Eclipse perspective, if you want to create a jar file from a java project you can right-click on the project and select “Export->Export…->Java->Jar”.  JDeveloper does not have an “Export” option, but this is covered by a Deployment Profile.  By default there is no Deployment Profile for the project, so create a new one and name it what you want the jar to be named.  You can see that this will jar up this project and place it in the project’s deploy directory.

jar [2]

You can explore this even further by looking at the File Groups->Project Output->Contributors and see that the Build Output and Project Dependencies both make up the contents of this jar.  Now that we have a jar, let’s turn to the LibraryWeb project that we want to use the LibraryUtility.jar.  Remember earlier when I tried to manually add the jar to the WEB-INF/lib directory?  I should have just used the “Project Properties->Libraries and Classpath” for the ViewController web module project and added the jar file to the classpath.  Notice that the “Export” checkbox is selected which means this jar will be packaged as part of the WAR under WEB-INF/lib.  So similar to the LibraryUtiilty project, you can create a Deployment Profile for the LibraryWeb project.  This time select WAR as the archive type.  You can see that the jar will be included in the WAR under the WEB-INF/lib contributors.

classpath

Contributors

zip

Notice that the Deployment Profile also has a menu option for Profile Dependencies.  There you can indicate that this Deployment Profile depends on the Deployment Profile that builds the jar for the LibraryUtility project.

libutility

You can follow similar steps to create a Deployment Profile for the Application via the Application Properties, this time selecting an EAR archive type.  Now we have a similar experience to Eclipse where I can right click on the project select Deploy->LibraryTestApp-> to EAR file.  In fact, I can get fancy in the Application’s Deployment Profile and specify the path to the LibraryUtility.jar file in the EAR to be in APP-INF/lib.  You can set up multiple deployment profiles per project or application, so it’s actually more flexible than the Eclipse structure.

deploy

If you notice the Deploy->LibraryTestApp-> as has a “to” option as well as “to EAR file”.  Using this option I can deploy this archive to a running WLS server, more detail on that in another entry.

An Alternative Approach – Project Dependencies

There is alternative to the Libraries and Classpath linked to Deployment Profile approach that JDeveloper actually uses by default for Fusion Web Applications.  When you create a new Fusion Web Application, a Model project and a ViewController project are created by default.  In ViewController->Project Properties->Dependencies you will see a dependency on the Model project. 

fusion

Now look at the auto-generated Deployment Profile for the ViewController project and you will see this causes the build output of the Model project to be added to the build output of the ViewController project because of the checkbox for “Project Dependencies” in the WEB-INF/classes contributors section.  This results in only one combined module inside the EAR, instead of two separate ones.  So which method is better – Project Dependencies or linking the Libraries and Classpath to another project’s Deployment Profile?  It is really a matter of taste, but since I had a preconceived notion for the structure of the EAR in the original use case, the Libraries and Classpath linked to the LibraryUtility project’s Deployment Profile worked best for me.

fusionDeploymentProfile

Summary

So the key take-aways are:

  • JDeveloper organizes Projects inside of Applications
  • Applications and Projects have the concept of Deployment Profiles and Dependencies, which can be used to structure the archives as we like them
  • Deployment Profiles can then deploy the archives directly to running servers as well as the file system

Hopefully this gives a good introduction to some of the distinctions for developing and packaging Enterprise Applications and Modules in Eclipse and JDeveloper.

Potential Future Topics

Here is a list of potential topics that I think are interesting for comparison’s sake, let me know if there are others you’d like to hear about.

  • IDE meta-data
  • IDE shortcuts
  • Monitoring network traffic
  • Deploying to a server
  • Facets versus Technology Scope