weblogic

Spring and WebLogic Durable Subscriber

Based on a previous post, it was evident that continuing with the use of DefaultMessageListenerContainer with Topics, that messages can be lost. In order to resolve this issue, it is necessary to utilize Spring’s configuration (along with WebLogic requirements) for configuring a durable subscriber. This is needed in order to guarantee that when the DefaultMessageListenerContainer disconnects based on the receiveTimeout, that messages are not lost. Durable subscriber will also guarantee that as multiple subscribers to the Topic are connected/disconnected, that they will still be able process messages received while they were disconnected.

To have a good understanding of durable and just general JMS concepts, a good older article to read is Guaranteed Messaging with JMS.

And before I get too much in the details, the definitions defined in this snippet (Persistent vs Durable JMS messages), helps define the difference between durable and persistent – just to make sure I don’t get the two confused:

A ” durable message ” is a message where the JMS server will hold on to a message if the subscriber is temporarily unavaliable. So the durability is defined by the relationship between a “Topc Subscriber” and the “JMS Server”. Durability is applicable only to publish/Subscribe paradigm. For this to happen subscribers need to register themselves with a unique ” client id “.

A ” persistent message ” is a message that defines the relationship between a “Message Producer” and the “JMS Server”. This can be established for both point-to-point and publish/subscribe. This has to do with the guaranteed once only delivery of the message by persisting the message after it has been recieved from the message producer.

The ActiveMQ documentation gives us a little more information as well(How do durable queues and topics work):

Durable queues keep messages around persistently for any suitable consumer to consume them. Durable queues do not need to concern themselves with which consumer is going to consume the messages at some point in the future. There is just one copy of a message that any consumer in the future can consume.

Durable topics however are different as they must logically persist an instance of each suitable message for every durable consumer – since each durable consumer gets their own copy of the message.

So I need to take the following configuration that I had for a subscribe service against a Topic and make it durable.

1
2
3
4
5
6
7
8
<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleMessagingConnectionFactory"
    p:destinationName="com/foo/dest/SampleTopic"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:pubSubDomain="true"
    p:transactionManager-ref="GlobalDataTransactionManager"/>

The interesting part is that quite a bit of time can be taken on Google trying to find information on how to do this and the Spring documentation does not really seem to describe what is necessary for durable subscription.

Here are a few resources with some details as to creating durable subscriber services via Spring:

Persistent JMS Topics using ActiveMQ and Spring

So based on this article and a few more resources, it is apparent that I need to add some additional pieces of configuration:

1
2
3
4
5
6
7
8
9
10
11
<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleMessagingConnectionFactory"
    p:destinationName="com/foo/dest/SampleTopic"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:pubSubDomain="true"
    p:subscriptionDurable="true"
    p:durableSubscriptionName="sampleSubscribeService"
    p:clientId="sampleSubscribeService"
    p:transactionManager-ref="GlobalDataTransactionManager"/>

So I add three pieces of configuration (subscriptionDurable, durableSubscriptionName, and clientId), and then I deploy this to my server. Interestingly enough, the deployment works but the application fails on startup with the following error:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
weblogic.jms.common.IllegalStateException: [JMSClientExceptions:055032]An attempt was made to create a named consumer (sampleSubscribeService) on a connection with no clientID
    at weblogic.jms.client.JMSSession.throwWhenInvalidSubscriberName(JMSSession.java:2770) ~[weblogic.jar:10.3.5.0]
    at weblogic.jms.client.JMSSession.setupConsumer(JMSSession.java:2723) ~[weblogic.jar:10.3.5.0]
    at weblogic.jms.client.JMSSession.createConsumer(JMSSession.java:2691) ~[weblogic.jar:10.3.5.0]
    at weblogic.jms.client.JMSSession.createDurableSubscriber(JMSSession.java:2487) ~[weblogic.jar:10.3.5.0]
    at weblogic.jms.client.WLSessionImpl.createDurableSubscriber(WLSessionImpl.java:1233) ~[weblogic.jar:10.3.5.0]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.createConsumer(AbstractPollingMessageListenerContainer.java:493) ~[spring-full.jar:3.0.5]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.createListenerConsumer(AbstractPollingMessageListenerContainer.java:223) ~[spring-full.jar:3.0.5]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:307) ~[spring-full.jar:3.0.5]
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:243) ~[spring-full.jar:3.0.5]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1059) [spring-full.jar:3.0.5]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1050) [spring-full.jar:3.0.5]
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:947) [spring-full.jar:3.0.5]
    at java.lang.Thread.run(Thread.java:662) [na:1.6.0_24]

So even though I defined a clientId within the configuration as is available through the Javadoc on DefaultMessageListenerContainer, I get hit with a clientID issue. If we go back to the ActiveMQ discussion we mentioned earlier, it gives some more information with regards to the importance of a clientID:

So for durable topic subscription, the JMS provider needs to be able to identify S when it shuts down and later on in the future reconnects, so it can know what messages to send to it while it was not running. JMS specification dictates that the identification of S is done by a combination of the clientID and the durable subscriber name. This is so that the JMS connection S uses can have many different durable subscriptions on different topics or on the same topic with different selectors – yet the JMS provider can know which message for which subscription to keep around for it.

So setting the clientID on a JMS connection is vital (along with using a sensible durable consumer name) for durable topic subscription.

So based upon this information, it appears that I need to have a clientId registered on the ConnectionFactory and not technically on the DefaultMessageListenerContainer (even though the API seems to allow it, it is not working based on the configuration I provided). So after quite a bit of searching, I found an article that I can’t seem to locate…but it points me towards SingleConnectionFactory which would allow me to wrap the ConnectionFactory that I am using and supply a clientId on that ConnectionFactory. Then I just utilize this new bean id (for the SingleConnectionFactory) in the configuration for my DefaultMessageListenerContainer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="sampleDurableTopicConnectionFactory"
    class="org.springframework.jms.connection.SingleConnectionFactory"
    p:targetConnectionFactory-ref="sampleMessagingConnectionFactory"
    p:clientId="sampleSubscribeService"/>

<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleDurableTopicConnectionFactory"
    p:destinationName="com/foo/dest/SampleTopic"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:pubSubDomain="true"
    p:subscriptionDurable="true"
    p:durableSubscriptionName="sampleSubscribeService"
    p:transactionManager-ref="GlobalDataTransactionManager"/>

So I passed in our JMS ConnectionFactory to the configuration of the SingleConnectionFactory, and moved the clientId configuration to it as well. Then I pass in the sampleDurableTopicConnectionFactory as the connectionFactory within the configuration for my DefaultMessageListenerContainer. I deploy this new web application and to my surprise, it still doesn’t work. I get the following error (which is the same error I get if I connect to a Uniform Distributed Topic (WLS) with HermesJMS:

1
2
3
4
5
6
7
8
weblogic.jms.common.JMSException: [JMSClientExceptions:055030]This topic does not support durable subscriptions
    at weblogic.jms.client.JMSSession.createDurableSubscriber(JMSSession.java:2482)
    at weblogic.jms.client.WLSessionImpl.createDurableSubscriber(WLSessionImpl.java:1233)
    at hermes.impl.TopicBrowser.getEnumeration(TopicBrowser.java:220)
    at hermes.browser.tasks.BrowseDestinationTask.invoke(BrowseDestinationTask.java:146)
    at hermes.browser.tasks.TaskSupport.run(TaskSupport.java:175)
    at hermes.browser.tasks.ThreadPool.run(ThreadPool.java:170)
    at java.lang.Thread.run(Unknown Source)

Now I mentioned this in a previous blog post, but with WebLogic Uniform Distributed Topics, you have to do special configuration within a Subscribe Service in order to use Durable Subscriber since WebLogic does not allow a Durable Subscriber against a Uniform Distributed Topic (unless you point to one of the specific members…by including a single JMS Server in the JNDI url…like jmsServer1@/com/foo/SampleBasicTopic). The reason being is explained in the WebLogic Server Documentation:

Note: Durable subscribers (DurableTopicSubscriber) cannot be created for distributed topics. However, you can still create a durable subscription on distributed topic member and the other topic members will forward the messages to the topic member that has the durable subscription.

So in order to make this work, I simply need to modify the destinationName to include the JMS Server name:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="sampleDurableTopicConnectionFactory"
    class="org.springframework.jms.connection.SingleConnectionFactory"
    p:targetConnectionFactory-ref="sampleMessagingConnectionFactory"
    p:clientId="sampleSubscribeService"/>

<bean id="sampleSubscribeServiceListenerContainer"  
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"
    p:connectionFactory-ref="sampleDurableTopicConnectionFactory"
    p:destinationName="jmsServer1@com/foo/dest/SampleTopic"
    p:destinationResolver-ref="sampleDestinationResolver"
    p:messageListener-ref="sampleSubscribeServiceMessageListener"
    p:pubSubDomain="true"
    p:subscriptionDurable="true"
    p:durableSubscriptionName="sampleSubscribeService"
    p:transactionManager-ref="GlobalDataTransactionManager"/>

I rebuild my war and deploy, and luckily this time, it works. I now have a durable subscriber. The interesting part is that as I start clicking around WebLogic console and look at the configured ConnectionFactory, I notice a Client Id configuration.

With this newly discovered piece of information, I find some more information that clarifies the role of the Client Id in Developing Advanced Pub/Sub Applications.

A subscription key is used to uniquely identify a subscription. For non-durable subscriptions, the key is composed of the Client ID and Client ID Policy. For durable subscriptions, the key is composed of the Client ID, Client ID Policy, and Subscription Name.

So this clarifies that I should just be able to set the Client Id on the ConnectionFactory within WebLogic and remove the configuration of the SingleConnectionFactory from my Spring configuration. So I went ahead and modified my ConnectionFactory by adding into the Client Id the value “sampleSubscribeService”, and then removed the SingleConnectionFactory configuration from my Spring Context file, rebuild/redeployed my web application, and my subscribe service works with this configuration as well. I can even go into the WebLogic Server console, click on the my JMS Resource (the Topic), go to Monitoring, and I should see a “Durable Subscribers” tab with the subscription name and client id that I defined.

Share and Enjoy

Deploying JMS/JDBC Resources with weblogic-maven-plugin

The weblogic-maven-plugin has given us the ability to easily deploy wars and also to deploy Optional Packages. I can use this capability of deploying wars to also deploy other artifacts such as JDBC and JMS Resources to our WebLogic Domain.

I can create JDBC and JMS Resources by logging into the WebLogic Domain and manually creating these resources (such as JDBC Data Sources and JMS Connection Factories/Queues/Topics). When the resources are created through the console, two modifications are done on the file system.

  1. %DOMAIN_HOME%\config\config.xml = contains configurations for domain and reference to JDBC Resource xml file
  2. %DOMAIN_HOME%\config\jdbc\%DATASOURCE_NAME%-%NUMBER%-jdbc.xml = the configuration for the data source

If I take a look at the jdbc.xml file, I will see the configuration which I created in the domain console:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version='1.0' encoding='UTF-8'?>
<jdbc-data-source xmlns="http://xmlns.oracle.com/weblogic/jdbc-data-source" xmlns:sec="http://xmlns.oracle.com/weblogic/security" xmlns:wls="http://xmlns.oracle.com/weblogic/security/wls" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/jdbc-data-source http://xmlns.oracle.com/weblogic/jdbc-data-source/1.0/jdbc-data-source.xsd">
  <name>Sample Data Source</name>
  <jdbc-driver-params>
    <url>jdbc:oracle:thin:@sampleDbHost:1521:sampleDbName</url>
    <driver-name>oracle.jdbc.xa.client.OracleXADataSource</driver-name>
    <properties>
      <property>
        <name>user</name>
        <value>sampleDbUserName</value>
      </property>
    </properties>
    <password-encrypted>Kk67O8FmBQBMN7ws5W+PkYRb9dy1E2hMLb195PQfvqg=</password-encrypted>
  </jdbc-driver-params>
  <jdbc-connection-pool-params>
    <test-table-name>SQL SELECT 1 FROM DUAL</test-table-name>
  </jdbc-connection-pool-params>
  <jdbc-data-source-params>
    <jndi-name>sampleJndiName</jndi-name>
    <global-transactions-protocol>TwoPhaseCommit</global-transactions-protocol>
  </jdbc-data-source-params>
</jdbc-data-source>

The password-encrypted field is the password that I entered in plain text that has been encrypted based on the domain’s salt file (SerializedSystemIni.dat). So I could essentially take this file and remove all the database specific properties, and do a merge with our environment properties (per environment), to create a jdbc.xml configuration file (a file that creates a data source per environment). I can do this by creating an XML file (from this configuration), putting all the properties into an environment specific property file, use the maven resources configuration to expand the properties into the XML file, and then use the weblogic-maven-plugin to deploy the merged configuration file. I can also use the encryption project I mentioned before to encrypt a plain text password against a salt to include within a properties file.

So all I need to do is to remove properties that are specific to environment or a single data source:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version='1.0' encoding='UTF-8'?>
<jdbc-data-source xmlns="http://xmlns.oracle.com/weblogic/jdbc-data-source"
    xmlns:sec="http://xmlns.oracle.com/weblogic/security"
    xmlns:wls="http://xmlns.oracle.com/weblogic/security/wls"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.oracle.com/weblogic/jdbc-data-source http://xmlns.oracle.com/weblogic/jdbc-data-source/1.0/jdbc-data-source.xsd">
    <name>sample</name>
    <jdbc-driver-params>
        <url>jdbc:oracle:thin:${at.symbol}(DESCRIPTION=(LOAD_BALANCE=yes)(ADDRESS=(PROTOCOL=TCP)(HOST=${sample.jdbc.hostname1})(PORT=${sample.jdbc.port}))(ADDRESS=(PROTOCOL=TCP)(HOST=${sample.jdbc.hostname2})(PORT=${sample.jdbc.port}))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=${sample.jdbc.service})))</url>
        <driver-name>oracle.jdbc.xa.client.OracleXADataSource</driver-name>
        <properties>
            <property>
                <name>user</name>
                <value>${sample.jdbc.username}</value>
            </property>
        </properties>
        <password-encrypted>${sample.jdbc.password.encrypted}</password-encrypted>
    </jdbc-driver-params>
    <jdbc-connection-pool-params>
        <initial-capacity>${sample.jdbc.capacity.initial}</initial-capacity>
        <max-capacity>${sample.jdbc.capacity.max}</max-capacity>
        <capacity-increment>1</capacity-increment>
        <test-connections-on-reserve>true</test-connections-on-reserve>
        <test-table-name>SQL SELECT 1 FROM DUAL</test-table-name>
        <statement-cache-size>10</statement-cache-size>
        <statement-cache-type>LRU</statement-cache-type>
    </jdbc-connection-pool-params>
    <jdbc-data-source-params>
        <jndi-name>${sample.jndi.name}</jndi-name>
        <global-transactions-protocol>TwoPhaseCommit</global-transactions-protocol>
    </jdbc-data-source-params>
    <jdbc-oracle-params>
        <fan-enabled>false</fan-enabled>
        <ons-node-list></ons-node-list>
        <ons-wallet-file></ons-wallet-file>
    </jdbc-oracle-params>
</jdbc-data-source>

I then use the custom ext properties plugin to read in the custom properties and then use the resource configuration to expand the properties in the file:

1
2
3
4
5
6
<resources>
    <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
    </resource>
</resources>

Now I have an JDBC configuration file built specifically per environment and specific JDBC resource, by supplying the specific environment during the maven runtime. And now that I have a JDBC configuration file, I need to be able to deploy it to an environment and I do this the same as how I deployed wars to an environment.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<plugin>
    <groupId>com.oracle.weblogic</groupId>
    <artifactId>weblogic-maven-plugin</artifactId>
    <version>10.3.4</version>
    <executions>
        <execution>
            <phase>compile</phase>
            <goals>
                <goal>deploy</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <adminurl>t3://${deploy.hostname}:${deploy.port}</adminurl>
        <user>${deploy.user}</user>
        <password>${deploy.password}</password>
        <upload>true</upload>
        <action>deploy</action>
        <targets>${target.names}</targets>
        <remote>true</remote>
        <verbose>true</verbose>
        <debug>true</debug>
        <name>${resource.name}${resource.suffix}</name>
        <source>${project.build.outputDirectory}/${deploy.env}/${folder.suffix}/${resource.name}${file.suffix}</source>
    </configuration>
</plugin>

I simply select the source location for the JDBC configuration and then deploy it with a name such as sampleDS. The data source will now be registered as an Application Data Source, which is different than a System Data Source (since I don’t seem to be able to use Application Data Sources with the creation of a JDBC Persistent Store for JMS).

Now that I understand how to do this with JDBC configurations, I can do it with JMS configuration as well. JMS gets more complicated though based on how WebLogic structures the JMS components into deployable units, as well as the introduction to very unique resources (ConnectionFactory, Queue, Topic, and Store-and-Forward). Also, WebLogic supports a concept known as Uniform Distributed Queue and Uniform Distributed Topic, which has different configurations within the XML file. Also, WebLogic requires specific targeting when deploying resources.

So let’s talk about some JMS best practices first, and these are also some great resources:

Next, let’s talk about some of the deployment and setup information I need:

  1. A JMS Server should be created for each Managed Server instance
  2. In order to have a JMS Server deploy to a Managed Server (migratable), I must not use the default store
  3. A Store-And-Forward Agent should be created for each Managed Server instance
  4. JMS Connection Factories should use default-targeting-enabled=true so they are targeted to the cluster
  5. Queues/Topics/SAF Imported Destinations should name the XML file as the subdeployment
  6. Subdeployments should be targeted at the JMS Server (one for each Managed Server) or Store-And-Forward Agents

So a sample file would look like this (without Store-And-Forward):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?xml version='1.0' encoding='UTF-8'?>
<weblogic-jms xmlns="http://xmlns.oracle.com/weblogic/weblogic-jms" xmlns:sec="http://xmlns.oracle.com/weblogic/security" xmlns:wls="http://xmlns.oracle.com/weblogic/security/wls" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-jms http://xmlns.oracle.com/weblogic/weblogic-jms/1.1/weblogic-jms.xsd">
  <connection-factory name="SampleConnectionFactory">
    <default-targeting-enabled>true</default-targeting-enabled>
    <jndi-name>com/foo/SampleConnectionFactory</jndi-name>
    <client-params>
      <client-id-policy>Restricted</client-id-policy>
      <subscription-sharing-policy>Exclusive</subscription-sharing-policy>
      <messages-maximum>10</messages-maximum>
    </client-params>
    <transaction-params>
      <xa-connection-factory-enabled>${sampleJMS.jms.xa.enabled}</xa-connection-factory-enabled>
    </transaction-params>
    <security-params>
      <attach-jmsx-user-id>false</attach-jmsx-user-id>
    </security-params>
  </connection-factory>
  <uniform-distributed-queue name="SampleBasicQueue">
    <sub-deployment-name>sampleJmsSubdeployment</sub-deployment-name>
    <delivery-params-overrides>
      <redelivery-delay>${sampleBasicQueue.jms.redelivery.delay}</redelivery-delay>
    </delivery-params-overrides>
    <delivery-failure-params>
      <error-destination>SampleBasicQueueErrorQueue</error-destination>
      <redelivery-limit>${sampleBasicQueue.jms.redelivery.limit}</redelivery-limit>
      <expiration-policy>Redirect</expiration-policy>
    </delivery-failure-params>
    <jndi-name>com/foo/dest/SampleBasicQueue</jndi-name>
    <saf-export-policy>${sampleBasicQueue.jms.saf.policy}</saf-export-policy>
  </uniform-distributed-queue>
  <uniform-distributed-queue name="SampleBasicQueueErrorQueue">
    <sub-deployment-name>sampleJmsSubdeployment</sub-deployment-name>
    <jndi-name>com/foo/dest/SampleBasicQueueErrorQueue</jndi-name>
  </uniform-distributed-queue>
  <uniform-distributed-queue name="SampleBasicTopicErrorQueue">
    <sub-deployment-name>sampleJmsSubdeployment</sub-deployment-name>
    <jndi-name>com/foo/dest/SampleBasicTopicErrorQueue</jndi-name>
  </uniform-distributed-queue>
  <uniform-distributed-topic name="SampleBasicTopic">
    <sub-deployment-name>sampleJmsSubdeployment</sub-deployment-name>
    <delivery-params-overrides>
      <redelivery-delay>${sampleBasicTopic.jms.redelivery.delay}</redelivery-delay>
    </delivery-params-overrides>
    <delivery-failure-params>
      <error-destination>SampleBasicTopicErrorQueue</error-destination>
      <redelivery-limit>${sampleBasicTopic.jms.redelivery.limit}</redelivery-limit>
      <expiration-policy>Redirect</expiration-policy>
    </delivery-failure-params>
    <jndi-name>com/foo/dest/SampleBasicTopic</jndi-name>
    <saf-export-policy>${sampleBasicTopic.jms.saf.policy}</saf-export-policy>
    <forwarding-policy>Replicated</forwarding-policy>
  </uniform-distributed-topic>
</weblogic-jms>

And here is an example with Store-And-Forward, which has a Remote Context (the connection to the remote server that the message will be forwarded to), and Remote Imported Destinations are the Queue/Topics on the remote machine that will be receiving the messages:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version='1.0' encoding='UTF-8'?>
<weblogic-jms xmlns="http://xmlns.oracle.com/weblogic/weblogic-jms" xmlns:sec="http://xmlns.oracle.com/weblogic/security" xmlns:wls="http://xmlns.oracle.com/weblogic/security/wls" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-jms http://xmlns.oracle.com/weblogic/weblogic-jms/1.1/weblogic-jms.xsd">
  <connection-factory name="SampleConnectionFactory">
    <default-targeting-enabled>true</default-targeting-enabled>
    <jndi-name>com/foo/SampleConnectionFactory</jndi-name>
    <client-params>
      <client-id-policy>Restricted</client-id-policy>
      <subscription-sharing-policy>Exclusive</subscription-sharing-policy>
      <messages-maximum>10</messages-maximum>
    </client-params>
    <transaction-params>
      <xa-connection-factory-enabled>${sampleJMS.jms.xa.enabled}</xa-connection-factory-enabled>
    </transaction-params>
    <security-params>
      <attach-jmsx-user-id>false</attach-jmsx-user-id>
    </security-params>
  </connection-factory>
  <saf-imported-destinations name="SampleSAFImportedDestination">
        <sub-deployment-name>sampleJmsSubdeployment</sub-deployment-name>
        <saf-topic name="SampleEventTopic">
            <remote-jndi-name>com/foo/dest/SampleEventTopic</remote-jndi-name>
            <saf-error-handling>SampleEventErrorHandling</saf-error-handling>
        </saf-topic>
        <jndi-prefix xsi:nil="true"></jndi-prefix>
        <saf-remote-context>SampleRemoteSAFContext</saf-remote-context>
        <saf-error-handling>SampleErrorHandling</saf-error-handling>
        <time-to-live-default>0</time-to-live-default>
        <use-saf-time-to-live-default>false</use-saf-time-to-live-default>
  </saf-imported-destinations>
  <saf-remote-context name="SampleRemoteSAFContext">
        <saf-login-context>
          <loginURL>${remote.wls.jms.url}</loginURL>
          <username>${remote.wls.userId}</username>
          <password-encrypted>${remote.wls.password.encrypted}</password-encrypted>
        </saf-login-context>
        <compression-threshold>2147483647</compression-threshold>
    </saf-remote-context>
   <saf-error-handling name="SampleErrorHandling">
    <policy>Log</policy>
    <log-format xsi:nil="true"></log-format>
    <saf-error-destination xsi:nil="true"></saf-error-destination>
  </saf-error-handling>
   <saf-error-handling name="SampleEventErrorHandling">
    <policy>Log</policy>
    <log-format xsi:nil="true"></log-format>
    <saf-error-destination xsi:nil="true"></saf-error-destination>
  </saf-error-handling>
</weblogic-jms>

So for deploying JMS Resources, there is an additional piece of configuration that I need to add to the plugin. I have built that through a profile incase I have one -> many Managed Servers based on the environment I am deploying to. So the configuration looks the same as the JDBC configuration, except for the inclusion of this profile that contains a submoduletargets property.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  <profile>
    <id>mgdServers1</id>
    <properties>
    <submoduletargets>
       sampleJmsSubdeployment@jmsServer1
        </submoduletargets>
    </properties>
  </profile>
  <profile>
    <id>mgdServers2</id>
    <properties>
    <submoduletargets>
      sampleJmsSubdeployment@jmsServer1,sampleJmsSubdeployment@jmsServer2
        </submoduletargets>
    </properties>
  </profile>

And this when deploying to Store-And-Forward Agents, I just change the name from the JMS Server name to the Agent name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  <profile>
    <id>mgdServers1</id>
    <properties>
    <submoduletargets>
       sampleJmsSubdeployment@safAgent1
        </submoduletargets>
    </properties>
  </profile>
  <profile>
    <id>mgdServers2</id>
    <properties>
    <submoduletargets>
      sampleJmsSubdeployment@safAgent1,sampleJmsSubdeployment@safAgent2
        </submoduletargets>
    </properties>
  </profile>

Of course the names of the JMS Servers and/or Store-And-Forward Agents need to match what you have the WebLogic Server resources named. Now when I deploy the resources, you will see the ConnectionFactory targeted to the Cluster, while the Queues/Topics/SAF resources are grouped within a subdeployment and that subdeployment is targeted to a JMS Server or SAF Agent. So if I change the targeted JMS Server or SAF Agent, every resource within that subdeployment will change its target.

One last thing to mention, especially with WebLogic Uniform Distributed Topics, is that you have to do special configuration within a Subscribe Service in order to use Durable Subscribe since WebLogic does not allow a Durable Subscribe against a Uniform Distributed Topic (unless you point to one of the specific members…by including a single JMS Server in the JNDI url…like jmsServer1@/com/foo/SampleBasicTopic). I will cover this in another posting.

dapoksetin

Share and Enjoy

WebLogic Optional Packages and Dependency Maturity

So I mentioned in another post about a Dependency Maturity Model where I will pull the environmental properties from our WAR deployments. The way I accomplish this with WebLogic is by making the jar file that holds the environmental property like an OSGi bundle for WebLogic (which is an optional package). This is not a very complex exercise, it is really about giving a JAR information with it’s MANIFEST.MF to identify it’s versions and a JAR key, then referencing that within deployments that use the JAR (like a WAR).

I configure the maven-jar-plugin to put additional information to the MANIFEST.MF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifestEntries>
                <Manifest-Version>1.0</Manifest-Version>
                <Extension-Name>envProperties</Extension-Name>
                <Specification-Version>${env.spec.version}</Specification-Version>
                <Implementation-Version>${env.impl.version}</Implementation-Version>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

Once this JAR is built through our Maven project, I need to deploy it out to WebLogic utilizing the maven-weblogic-plugin (for WLS 11). There are two interesting pieces of information regarding the link to the WebLogic Server 11 and WebLogic Server 12 versions of the plugin. The WebLogic Server 12 version appears to have a bug and does not support deploying libraries at all and therefore I have to utilize the WebLogic Server 11 version of the maven plugin. The second issue though is that the WebLogic Server 11 version of the plugin does not support the library capability through XML, it has to be supplied as a command line argument.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<plugin>
    <groupId>com.oracle.weblogic</groupId>
    <artifactId>weblogic-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>deploy</id>
            <phase>compile</phase>
            <goals>
                <goal>deploy</goal>
            </goals>
            <configuration>
                <adminurl>t3://${target.hostname}:${target.port}</adminurl>
                <user>${target.userId}</user>
                <password>${target.password}</password>
                <upload>true</upload>
                <action>deploy</action>
                <targets>${target.cluster}</targets>
                <remote>true</remote>
                <verbose>true</verbose>
                <library>true</library>
                <source>${project.build.directory}/lib/${property.name}.jar</source>
                <name>envProperties</name>
            </configuration>
        </execution>
    </executions>
</plugin>

So the main piece of configuration here that differentiates this from a normal WAR or EAR deployment is the true. This XML element is supposed to identify this deployment as an Optional Package, but this field does not work. In order to bypass this error, when I run the maven goal to fire off this plugin, I have to pass in a -Dlibrary=true for this resource to successfully deploy as an Optional Package (otherwise WebLogic will generate an error that this is an invalid resource type).

The next step is to make modifications to our WAR so that it can become aware of the Optional Package. I do this by modifying a maven plugin so that it can build references in the WAR’s MANIFEST.MF to the JAR’s MANIFEST.MF.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <configuration>
        <archive>
            <manifestEntries>
                <Manifest-Version>1.0</Manifest-Version>
                                           
                <Extension-List>envProperties cxfFull</Extension-List>
                <envProperties-Extension-Name>envProperties</envProperties-Extension-Name>
                <envProperties-Specification-Version>${env.spec.version}</envProperties-Specification-Version>
                <envProperties-Implementation-Version>${env.spec.version}</envProperties-Implementation-Version>
                                           
                <cxfFull-Extension-Name>cxfFull</cxfFull-Extension-Name>
                <cxfFull-Implementation-Version>${cxfFull.spec.version}</cxfFull-Implementation-Version>
                <cxfFull-Specification-Version>${cxfFull.spec.version}</cxfFull-Specification-Version>
            </manifestEntries>
        </archive>
        <packagingExcludes>WEB-INF/lib/cxf*.jar</packagingExcludes>
    </configuration>
</plugin>

There are some additional interesting items that I have in this configuration that I can go over, but let’s first talk about how I reference the environment property JAR.

1
2
3
4
<Extension-List>envProperties cxfFull</Extension-List>
<envProperties-Extension-Name>envProperties</envProperties-Extension-Name>
<envProperties-Specification-Version>${env.spec.version}</envProperties-Specification-Version>
<envProperties-Implementation-Version>${env.spec.version}</envProperties-Implementation-Version>

In the Extension-List tag, I list all the *-Extension-Name configurations that I have. So in the previous section of the configuration, I have an envProperties-Extension-Name=envProperties and a cxfFull-Extension-Name=cxfFull. I therefore need to list these values in the Extension-List element. The next step is to reference the Implementation/Specification Versions and I do this by using the *-Extension-Name value as the key in the name of the configuration. So if our key is envProperties, our *-Extension-Name becomes envProperties-Extension-Name, our *-Specification-Version becomes envProperties-Specification-Version, and our *-Implementation-Version becomes envProperties-Implementation-Version.

The other important piece of configuration here is the . I utilize this feature to remove jars (that I have designated as Optional Packages) so that they will not be included into the built war file. The reason for this again is that these jars will be available via Optional Packages and therefore I remove them from the war and therefore reduce the size of the war and increase performance for startup/deployment.

Now that I have been able to create the reference between the two, once the WAR is deployed, it will create a link between it and the environment properties JAR. In fact, if I log into our WebLogic console and click on our environment properties deployment and I can see all the other deployments that are using it. These relationships will be listed under the “Applications that reference this Library” table.

Because of this relationship, anytime I need to redeploy the environmental properties jar or any Optional Package, I will need to start/stop the web applications that use the specific Optional Package. This allows that web application to reload the new changes to the Optional Package (unfortunately it does not appear to be dynamic), but it also does not require us to restart the Admin/Managed Server instances.

For more additional information about the weblogic-maven-plugin, see my other post as to how I deploy wars using this plugin.

Share and Enjoy