January 20, 2010

Migrating Resource Adapters from OC4J to WebLogic, Part 2 Inbound

Resource adapters can be classified into three categories: outbound, inbound and bi-directional. Previous article has shown you how to deal with outbound case. In this article, we will teach you how to deal with inbound case. It would be easy to understand how to deal with bi-directional case after reading outbound case and inbound case.

 

l       Mapping for Inbound Case

In inbound case, data flows from EIS to application server through adapters. According to connector 1.5 specification, an adapter can deliver inbound messages to MDB. This requires application server to provide its solution to associate the MDB with adapter. On OC4J, such association is defined in orion-ejb-jar.xml, while on WebLogic in weblogic-ejb-jar.xml. orion-ejb-jar.xml also contains configuration of activation spec.

 

Furthermore, it is normally necessary to define one or more admin objects in inbound case, such as the Queue, Topic etc. These admin object instances are defined in oc4j-connectors.xml on OC4J, and in weblogic-ra.xml on WebLogic.

 

You may have noticed that there is just one single descriptor file, weblogic-ra.xml, used on WebLogic for both inbound and outbound adapter. This is different on OC4J where 2 files are used: oc4j-ra.xml and oc4j-connectors.xml.

 

The mapping from OC4J to WebLogic involves 2 steps: fist, map oc4j-connectors.xml to weblogic-ra.xml; second, map orion-ejb-jar.xml to weblogic-ejb-jar.xml and ejb-jar.xml.

 

 

l       Map oc4j-connectors.xml

Let's map oc4j-connectors.xml to weblogic-ra.xml first.

Here is a sample oc4j-connectors.xml descriptor of OC4J which shows a typical configuration for inbound. For standalone adapters, there is one single global file existing for all standalone adapters, and the default location is j2ee/<instance>/config/oc4j-connectors.xml.

<?xml version="1.0" encoding="UTF-8"?>

 

<oc4j-connectors xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://xmlns.oracle.com/oracleas/schema/oc4j-connectors-10_0.xsd" schema-major-version="10" schema-minor-version="0" >

           <connector name="fooConnector" path="fooConnector.rar">

                     <adminobject-config location="eis/MyAdminObject">

                                <adminobject-class>foo.AdminObjImpl</adminobject-class>

                                <config-property name="host" value="localhost"/>

                                <config-property name="port" value="1234"/>

                     </adminobject-config>

           </connector>

 

</oc4j-connectors>

 

Each adminobject-config element defines an admin object instance. It should be mapped to weblogic-connector\admin-objects\admin-object-group\admin-object-instance element on WebLogic. Note: One different is that each admin object instance is identified by adminobject-class on OC4J but by admin-object-interface on WebLogic. So it is necessary to check ra.xml to find out the corresponding adminobject-interface value. This difference is caused by the fact that Connector 1.5 specification doesn't define uniqueness of admin object. In the latest Connector 1.6 specification, it is clarified that the combination of adminobject-interface and adminobject-class must be unique in ra.xml.

 

The location attribute defines where the admin object instance will be bind to JNDI. This should be mapped to admin-object-instance\jndi-name element on WebLogic.

 

Each property of this admin object instance is defined by a config-property element. It can be mapped to admin-object-instance\ properties\ property element

 

Here is the result after performing above mapping, and the result file should be packaged as META-INF\weblogic-ra.xml in the RAR file.

<weblogic-connector xmlns="http://www.bea.com/ns/weblogic/90">

           <jndi-name>fooConnector</jndi-name>

           <enable-global-access-to-classes>true</enable-global-access-to-classes>

          

           <admin-objects>

                     <admin-object-group>

                                <admin-object-interface>foo.AdminObjIntf</admin-object-interface>

                                <admin-object-instance>

                                          <jndi-name>eis/MyAdminObject</jndi-name>

                                          <properties>

                                                     <property>

                                                               <name>host</name>

                                                               <value>localhost</value>

                                                     </property>

                                                     <property>

                                                               <name>port</name>

                                                               <value>1234</value>

                                                     </property>

                                          </properties>

                                </admin-object-instance>

                     </admin-object-group>

           </admin-objects>   

 

</weblogic-connector>

 

l       Map orion-ejb-jar.xml

Now let's do the second step, map orion-ejb-jar.xml to weblogic-ejb-jar.xml.

 

Here is a sample orion-ejb-jar.xml descriptor of OC4J which associates MDB with adapter, and defines 2 properties for activation spec. It should be packaged as META-INF\orion-ejb-jar.xml in the MDB's jar file.

<?xml version="1.0" encoding="utf-8"?>

<orion-ejb-jar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://xmlns.oracle.com/oracleas/schema/orion-ejb-jar-10_0.xsd">

      <enterprise-beans>

            <message-driven-deployment name="myMDB" destination-location="ejb/myMDB" resource-adapter="fooConnector">

                                <config-property>

                                           <config-property-name>host</config-property-name>

                                           <config-property-value>192.168.1.1</config-property-value>

                                </config-property>

                                <config-property>

                                           <config-property-name>port</config-property-name>

                                           <config-property-value>2001</config-property-value>

                                </config-property>

            </message-driven-deployment>

      </enterprise-beans>

</orion-ejb-jar>

The key element is resource-adapter="fooConnector" which specifies the adapter with which this MDB should associate. You may have noticed that "fooConnector" is the name of the connector, either defined by name="fooConnector" in oc4j-connectors.xml, or by connector-name="fooConnector" in oc4j-ra.xml. WebLogic takes different approach with OC4J. On WebLogic, adapter can be bound to JNDI using element weblogic-connector\jndi-name, and MDB can specify the JNDI name of the adapter that it wants to associate with using element weblogic-enterprise-bean\message-driven-descriptor\resource-adapter-jndi-name.

 

Each config-property element defines one name/value for a property of the activation spec for this MDB. These values are used to override the values defined in enterprise-beans\message-driven\activation-config\activation-config-property elements in ejb-jar.xml. WebLogic doesn't support such override now and just uses values defined in ejb-jar.xml. So in above case, the ejb-jar.xml contained in MDB jar must be modified to take the value defined in orion-ejb-jar.xml.

 

Here is the result weblogic-ejb-jar.xml after performing above mapping, and the result file should be packaged as META-INF\weblogic-ejb-jar.xml in the MDB jar file.

<?xml version = "1.0" encoding = "UTF-8"?>

<weblogic-ejb-jar xmlns="http://xmlns.oracle.com/weblogic/weblogic-ejb-jar" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-ejb-jar" http://xmlns.oracle.com/weblogic/weblogic-ejb-jar/1.0/weblogic-ejb-jar.xsd ">

           <weblogic-enterprise-bean>

                     <ejb-name>myMDB</ejb-name>

                     <message-driven-descriptor>

                                <resource-adapter-jndi-name>fooConnector</resource-adapter-jndi-name>

                     </message-driven-descriptor>

                     <jndi-name>ejb/myMDB</jndi-name>

           </weblogic-enterprise-bean>

</weblogic-ejb-jar>

 

Here is the ejb-jar.xml which contains the values of activation spec defined in orion-ejb-jar.xml, and the file should be packaged as META-INF\ ejb-jar.xml in the MDB jar file.

<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" version="3.0" >

<enterprise-beans>

                  <message-driven>

                           <ejb-name>myMDB</ejb-name>

                           <ejb-class>foo.MyMDBImpl</ejb-class>

                           <messaging-type>foo.MyMessageLisener</messaging-type>

                           <transaction-type>Container</transaction-type>

                           <activation-config>

                                    <activation-config-property>

                                             <activation-config-property-name>host</activation-config-property-name>

                                              <activation-config-property-value>192.168.1.1</activation-config-property-value>

                                    </activation-config-property>

                                    <activation-config-property>

                                             <activation-config-property-name>port</activation-config-property-name>

                                             <activation-config-property-value>2001</activation-config-property-value>

                                    </activation-config-property>

                           </activation-config>

 ...

                  </message-driven>

         </enterprise-beans>

</ejb-jar>

 

 

After performing above steps, the mapping for inbound case completes.

 

l       Mapping for Bi-Directional Case

If the adapter is bi-directional that supports both outbound and inbound, there will be 3 descriptors on OC4J:

n       oc4j-ra.xml, for outbound, inside RAR

n       oc4j-connectors.xml, for inbound, inside RAR

n       orion-ejb-jar.xml, for inbound, inside MDB JAR

 

They should be mapped to 3 descriptors on WebLogic:

n       weblogic-ra.xml, for both outbound and inbound, inside RAR

n       weblogic-ejb-jar.xml, for inbound, inside MDB JAR

n       ejb-jar.xml, for activation spec configurations of inbound, inside MDB JAR

 

You can figure out how to do mapping easily based on the information in outbound section and inbound section.

 

 

l       Reference

Detailed information on configuring and developing resource adapters on WebLogic is available at Programming Resource Adapters for Oracle WebLogic Server. You can find detailed explanation for weblogic-ra.xml schema in appendix "A. weblogic-ra.xml Schema"

 

Information on MDB is available at Programming Enterprise JavaBeans for Oracle WebLogic Server and Programming Enterprise JavaBeans, Version 3.0 for Oracle WebLogic Server

 

January 14, 2010

EJB3: Mapping of One-to-One relationships when primary key in the source table is also a foreign key for the target entity

This blog entry will explain how to define the EJB3 object relational mappings for entities that have a 1-1 association when the primary key of the relationship's owner side is also the foreign key for the owned entity.

Consider the following diagram.

supplier-schema.JPG

The Supplier entity has a 1-1 relationship with the SupplierStatistics entity. A Supplier is identified by the ID key uniquely which is also the primary key of the SUPPLIER table. Each Supplier has an associated set of Statistics like the number of orders placed, accepted and delivered on time by a given Supplier. These statistics are persisted in the SUPPLIER_STATISTICS table. The records in the SUPPLIER and SUPPLIER_STATISTICS table are joined by the ID of the supplier. The ID column in the SUPPLIER table is also a foreign key which references the SUPPLIER_ID column in the SUPPLIER_STATISTICS table.

 

The EJB3 entity classes for the above relationship are given below.

 

package com.tutorial.entity;

 

import java.io.Serializable;

import javax.persistence.*;

 

/**

 * The persistent class for the SUPPLIER database table.

 *

 */

@Entity

public class Supplier implements Serializable {

  private static final long serialVersionUID = 1L;

 

  @Id

  @GeneratedValue(strategy=GenerationType.AUTO)

  private long id;

 

  private String name;

 

  //uni-directional one-to-one association to SupplierStatistic

  @OneToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH})

  @JoinColumn(name="ID", insertable=false, updatable=false)

  private SupplierStatistic supplierStatistic;

 

  public Supplier() {

      supplierStatistic = new SupplierStatistic();

  }

 

  public long getId() {

    return this.id;

  }

 

  public void setId(long id) {

    this.id = id;

  }

 

  public String getName() {

    return this.name;

  }

 

  public void setName(String name) {

    this.name = name;

  }

 

  public SupplierStatistic getSupplierStatistic() {

    return this.supplierStatistic;

  }

 

  @PrePersist

  public void initializeSupplierStatistic() {

    this.supplierStatistic.setSupplierId(id);

  }

     

}

 

package com.tutorial.entity;

 

import java.io.Serializable;

import javax.persistence.*;

import java.math.BigDecimal;

 

/**

 * The persistent class for the SUPPLIER_STATISTICS database table.

 */

@Entity

@Table(name="SUPPLIER_STATISTICS")

public class SupplierStatistic implements Serializable {

  private static final long serialVersionUID = 1L;

 

  @Id

  @GeneratedValue(strategy=GenerationType.AUTO)

  @Column(name="SUPPLIER_ID")

  private long supplierId;

 

  @Column(name="ORDERS_ACCEPTED")

  private BigDecimal ordersAccepted;

 

  @Column(name="ORDERS_DELIVERED_ON_TIME")

  private BigDecimal ordersDeliveredOnTime;

 

  @Column(name="ORDERS_PLACED")

  private BigDecimal ordersPlaced;

 

  SupplierStatistic() {}

 

  public long getSupplierId() {

    return this.supplierId;

  }

 

  public void setSupplierId(long supplierId) {

    this.supplierId = supplierId;

  }

 

  public BigDecimal getOrdersAccepted() {

    return this.ordersAccepted;

  }

 

  public void setOrdersAccepted(BigDecimal ordersAccepted) {

    this.ordersAccepted = ordersAccepted;

  }

 

  public BigDecimal getOrdersDeliveredOnTime() {

    return this.ordersDeliveredOnTime;

  }

 

  public void setOrdersDeliveredOnTime(BigDecimal ordersDeliveredOnTime) {

    this.ordersDeliveredOnTime = ordersDeliveredOnTime;

  }

 

  public BigDecimal getOrdersPlaced() {

    return this.ordersPlaced;

  }

 

  public void setOrdersPlaced(BigDecimal ordersPlaced) {

    this.ordersPlaced = ordersPlaced;

  }

 

}

 

Notice the salient features of the entity mappings.

 

  • The relationship is defined as a One-way 1-1 mapping from the Supplier, which is the owner of the relationship to the SupplierStatistic entity.

 

      //uni-directional one-to-one association to SupplierStatistic

@OneToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH})

@JoinColumn(name="ID", insertable=false, updatable=false)

private SupplierStatistic supplierStatistic;


The @JoinColumn annotation has the attributes insertable=false and updatable=false. This is because the ID column which is a primary key also happens to be the foreign key for the SupplierStatistic entity. Since we don't need to persist an additional column to persist the relationship which is implicitly defined by the primary key, we mark the JoinColumn as insertable and updateable = false. Otherwise you will run into errors depending upon your JPA provider.

 

With EclipseLink JPA provider available with Oracle Enterprise Pack for Eclipse (OEPE) I was getting the following error without these attributes.

 

javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 1.1.2.v20090612-r4475): org.eclipse.persistence.exceptions.DatabaseException

Internal Exception: java.sql.SQLSyntaxErrorException: ORA-00957: duplicate column name

 

Error Code: 957

Call: INSERT INTO SUPPLIER (ID, NAME, ID) VALUES (?, ?, ?)

      bind => [4502, SupplierName1, 4502]

 

  • The supplierStatistic entity is implicitly created when the Supplier is created. Refer to the constructor. The OneToOne relationship is marked as CascadeType.PERSIST so the SupplierStatistic entity will be persisted along with the Supplier entity.


    public Supplier() {

    supplierStatistic = new SupplierStatistic();

  }


  • What about the SUPPLIER_ID column value for the SupplierStatistic entity? It needs to be the same value as the parent Supplier's ID value. This is achieved by injecting the ID of the parent into the child before persisting them. The initialiazeSupplierStatistic method is annotated with the @PrePresist annonation.

 

     @PrePersist

     public void initializeSupplierStatistic() {

       this.supplierStatistic.setSupplierId(id);

     }

 

This ensures that the id field value that has been either auto-generated or retrieved from the sequence table (depending upon the generation strategy for the primary key) by the JPA provider is passed onto the child entity thereby defining the 1-1 association.


  • As a best practice the constructor for the SupplierStatistic class is not public, so there is no explicit way to create and persist a SupplierStatistic entity. It gets created and persisted only in the context of the parent. Correspondingly a SupplierStatistic entity is removed along with the parent Supplier, since we also have CascadeType.REMOVE on the relationship.

 

This pattern can be applied to other situations where the same primary key value is shared between the two entities, for example Customer and Contact which can share the CUSTOMER_ID has the common key.

 

Appendix

 

DDL for the SUPPLIER and SUPPLIER_STATISTICS tables for the Oracle Database 10g Express Edition

 

CREATE TABLE "SUPPLIER" 
( "ID" NUMBER NOT NULL ENABLE, 
  "NAME" VARCHAR2(4000), 
  CONSTRAINT "SUPPLIER_PK" PRIMARY KEY ("ID") ENABLE, 
  CONSTRAINT "SUPPLIER_FK" FOREIGN KEY ("ID")
  REFERENCES "SUPPLIER_STATISTICS" ("SUPPLIER_ID") ENABLE
)
 
CREATE TABLE "SUPPLIER_STATISTICS" 
( "SUPPLIER_ID" NUMBER NOT NULL ENABLE, 
  "ORDERS_PLACED" NUMBER, 
  "ORDERS_ACCEPTED" NUMBER, 
  "ORDERS_DELIVERED_ON_TIME" NUMBER, 
  CONSTRAINT "SUPPLIER_STATISTICS_PK" PRIMARY KEY ("SUPPLIER_ID") ENABLE
)

Continue reading "EJB3: Mapping of One-to-One relationships when primary key in the source table is also a foreign key for the target entity" »

January 3, 2010

Migrating Resource Adapters from OC4J to WebLogic, Part 1 Outbound

Both OC4J and WebLogic server support Connector 1.5 specification. If customer's resource adapters were developed based on SPEC and don't depend on OC4J proprietary extensions or features, it would be relatively easy to migrate the adapters from OC4J to WebLogic. During migration, one of the key steps is mapping OC4J's proprietary descriptors to WebLogic's descriptors. Here in this article we will show you how to migrate the descriptors for standalone adapters.

 

Resource adapters can be classified into three categories: outbound, inbound or bi-directional. This article will show you how to deal with outbound case. Inbound case will be shown in next article. It would be easy to understand how to deal with bi-directional case after reading outbound case and inbound case.

 

l       Overview of Descriptors

Many adapters may just support outbound communication from application server to EIS. The descriptor used in this scenario is oc4j-ra.xml. It contains OC4J specific configuration for outbound communication such as connection factories/pools, and should be mapped to weblogic-ra.xml.

 

If an adapter also supports inbound communication from EIS to application server, there is another descriptor for adapter: oc4j-connectors.xml. It defines admin objects for inbound communication, and also should be mapped to weblogic-ra.xml.

 

So there is just one single descriptor file used on WebLogic: weblogic-ra.xml for all types of adapters: outbound, inbound, and bidirectional.

 

For inbound, MDB should be used as message endpoint in standard Java EE environment. In this case, orion-ejb-jar.xml is required for the MDB (note: this file is packaged inside MDB jar, not adapter). It associates MDB with adapter, and should be mapped to weblogic-ejb-jar.xml in the MDB jar.

 

l       Mapping for Outbound Case

Here is a sample descriptor of OC4J which show a typical configuration for outbound connection factory. It should be packaged as META-INF\oc4j-ra.xml in the RAR file.

<?xml version="1.0"?>

<oc4j-connector-factories xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.oracle.com/technology/oracleas/schema/oc4j-connector-factories-10_0.xsd" schema-major-version="10" schema-minor-version="0" >

 

     <connector-factory location="eis/fooConnectionFactory" connector-name="fooConnector">

                <config-property name="serverName" value="localhost"/>

                <config-property name="port" value="80"/>

                <connection-pooling use="private">

                          <property name="minConnections" value="1" />

                          <property name="initial-capacity" value="1" />

                          <property name="maxConnections" value="10" />

                </connection-pooling>

                <connectionfactory-interface>foo.bar.MyConnectionFactory</connectionfactory-interface>

     </connector-factory>

 

</oc4j-connector-factories>

 

One big difference is that an adapter instance is uniquely identified by connector-name on OC4J; but on WebLogic, the adapter java bean instance will be bound to JNDI and uniquely identified by its JNDI name. So, the connector-name="fooConnector" on OC4J should be mapped to <jndi-name>fooConnector</jndi-name> on WebLogic. Please note that enable-global-access-to-classes should be enabled on WebLogic in general so as to let other deployed applications to access this standalone adapter. You will see later that connector-name on OC4J and corresponding jndi-name on WebLogic is also used when associating MDB with adapter in inbound case.

 

On OC4J, each connection factory instance is defined by one connector-factory element. It should be mapped to outbound-resource-adapter\connection-definition-group\connection-instance element on WebLogic. There might be multiple connector-factory elements on OC4J. Correspondingly, they can be mapped to multiple connection-instance elements on WebLogic.

 

Each connection factory instance must have a connectionfactory-interface element. This is same for both OC4J and WebLogic. So it is just needed to map connector-factory\connectionfactory-interface on OC4J to outbound-resource-adapter\connection-definition-group\onnection-factory-interface on WebLogic.

 

It is same for OC4J and WebLogic that connection factory should be bind to JNDI, and the mapping is quite straightforward. The location="eis/fooConnectionFactory" defines the JNDI name for the connector-factory on OC4J and should be mapped directly to <jndi-name> element under connection-instance on WebLogic.

 

The mapping for config-property is also straightforward. Each property should be mapped to connection-instance\connection-properties\properties\property on WebLogic.

 

The mapping for connection-pooling is a little complex, since OC4J and WebLogic have different set of configurations for the connection pool and each has some options that are hard to be mapped to the options of the other. One difference is that OC4J support "shared connection pool" concept that several connection factory instances can share a single connection pool; whereas on WebLogic each connection factory instance has its own connection pool. If, for example, 2 connection factory instances on OC4J share one connection pool, it would be required to define two separate connection-instance elements on WebLogic and each connection-instance will maintain its own connection pool internally. The maxConnections can be directly mapped to max-capacity on WebLogic and keep same effect. But the minConnections and initial-capacity are a little confusing and both can be mapped to initial-capacity on WebLogic. Other configurations may not be directly mapped to WebLogic straightforwardly. The detail description of all available configuration options for WebLogic's pool can be found at " Configuring Connection Pool Parameters" http://download.oracle.com/docs/cd/E12839_01/web.1111/e13732/connect.htm#i1251021

 

 

Here is the result after performing above mapping, and the result file should be packaged as META-INF\weblogic-ra.xml in the RAR file.

<?xml version = "1.0"?>

<weblogic-connector xmlns="http://www.bea.com/ns/weblogic/90">

 

           <jndi-name>fooConnector</jndi-name>

           <enable-global-access-to-classes>true</enable-global-access-to-classes>

   <outbound-resource-adapter>

    <connection-definition-group>

      <connection-factory-interface>foo.bar.MyConnectionFactory</connection-factory-interface>

      <connection-instance>

        <jndi-name>eis/fooConnectionFactory</jndi-name>

        <connection-properties>

          <pool-params>

            <initial-capacity>1</initial-capacity>

            <max-capacity>10</initial-capacity>

          </pool-params>

          <transaction-support>NoTransaction</transaction-support>

          <properties>
              <property>
                  <name>serverName</name>
                  <value>localhost</value>
              </property>
              <property>
                  <name>port</name>
                  <value>80</value>
              </property>
          </properties>
     </connection-properties>

      </connection-instance>

    </connection-definition-group>

   </outbound-resource-adapter>

 

</weblogic-connector>

 

You may have noted that WebLogic allows overriding transaction-support in weblogic-ra.xml while this is not allowed on OC4J. This allows extra useful flexibility in some cases. For example, if you have 1 connection-definition in ra.xml which supports LocalTransaction but some times you want to configure a connection factory instance and don't want it to participate in any global transaction, you can configure 2 connection-instances in weblogic-ra.xml and one as LocalTransaction while the other as NoTransaction.

 

l       Reference

Detailed information on configuring and developing resource adapters on WebLogic is available at Programming Resource Adapters for Oracle WebLogic Server. You can find detail explanation for weblogic-ra.xml schema in appendix "A. weblogic-ra.xml Schema"

December 1, 2009

Getting started with SCA

This blog is a primer on the new Weblogic SCA container introduced as a tech preview in Weblogic.

Like POJOs way too much and don't like the heavy weight JEE for application assembly? Good news is that Weblogic now houses a new SCA container! You can forever remain oblivious to nitty-gritty details of EJBs and Webservices and hide in the comfort of SCA bindings.

The SCA runtime is installed by default, but the container is imported as a shared library by SCA applications, so one needs to deploy the shared library into Weblogic first. This can be done via the command line:

java weblogic.Deployer -username <uname> -password <passwd> -deploy -library $WL_HOME/common/deployable-libraries/weblogic-sca-1.0.war

One can also use the Administration Console - remember to choose the Install this deployment as a library option.

SCA applications have three main parts:

1. POJO classes (instead of EJBs or Webservices), that house the business logic
2. A spring context file that wires up the POJOs, and declares SCA services/references with appropriate bindings
3. weblogic.xml (for WARs) or weblogic-application.xml (for EARs) where the weblogic-sca shared library is imported

Note that there is no explicit SCDL file. The spring context file itself accomodates the bindings. The coolest part is that the same spring context file and POJO classes, with no changes, can act as a component in a larger composite in Oracle SOA. So if you like some BPEL artifacts thrown into your application, you'll typically upgrade to Oracle SOA, but there is no need to discard or even rewrite the POJO artifacts! In Oracle SOA, there is an explicit SCDL file where bindings are declared, so the bindings in the spring context file will be moot though.

Lets take a typical JEE shopping cart application: ShoppingCartEJB making use of a helper class, which in turn making use of an external webservice. SCA can achieve this using plain POJOs, or beans in Spring parlance. So, we need two beans, a CartImpl class and a CartHelperImpl class. The POJO class CartImpl can be exposed as a SCA service using the sca:service element in the spring context file. This service will function the same as the ShoppingCartEJB via a binding.ejb. The POJO class CartHelperImpl requires an external WS reference. Note how this is referred to just like another bean. For a reference with a binding.ws, the type used must be a JAX-WS compatible interface, generated from the external WSDL using a tool such as Weblogic clientgen.

Sample spring-context.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
    xmlns:wlsb="http://xmlns.oracle.com/weblogic/weblogic-sca-binding"
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
        http://xmlns.oracle.com/weblogic/weblogic-sca
        http://xmlns.oracle.com/weblogic/weblogic-sca/1.0/weblogic-sca.xsd
        http://xmlns.oracle.com/weblogic/weblogic-sca-binding
        http://xmlns.oracle.com/weblogic/weblogic-sca-binding/1.0/weblogic-sca-binding.xsd">

<sca:service name="CartService" type="my.org.Cart" target="Cart">
    <wlsb:binding.ejb uri="/ShoppingCartEJB"/>
</sca:service>

<bean id="Cart" class="my.org.CartImpl">
    <property name="helper" ref="CartHelper"/>
</bean>

<bean id="CartHelper" class="my.org.CartHelperImpl">
    <property name="wsref" ref="CartReference"/>
</bean>

<sca:reference name="CartReference" type="my.org.CartRef">
    <wlsb:binding.ws uri="/ShoppingCartEJB"/>
</sca:reference>

</beans>

How does Weblogic know that this is a SCA application and not a regular Spring application? The key is the shared library import in weblogic-application.xml or weblogic.xml. This makes it a SCA application, and the container will look for META-INF/jsca/spring-context.xml resource.

Sample weblogic.xml:

<?xml version="1.0" encoding="UTF-8859-1"?>
<weblogic-application xmlns="http://www.bea.com/ns/weblogic/90">
  <library-ref>
    <library-name>weblogic-sca</library-name>
  </library-ref>
</weblogic-application>

Sample Spring SCA application WAR:

WEB-INF/web.xml
WEB-INF/weblogic.xml
WEB-INF/lib/spring.jar
WEB-INF/lib/commons-logging.jar
WEB-INF/lib/MyScaClasses.jar
        META-INF/jsca/spring-context.xml
        my/org/Cart
        my/org/CartImpl
        my/org/CartHelper
        my/org/CartHelperImpl
        my/org/CartRef

This SCA application can be deployed into WLS:

java weblogic.Deployer -username <uname> -password <passwd> -deploy samplescaapp.war

Again, the Adminstration Console can be used here as well.

Weblogic SCA container will consume this WAR file, and CartImpl will mimic the ShoppingCartEJB behavior to the outside world.

Threads Constraints in Work Managers

WLS work manager configuration supports three different types of constraints - minimum threads, maximum threads, and capacity constraints. Here we are going to focus on some issues related to using the minimum and maximum threads constraints.

First a brief introduction on the self-tuning thread pool in WLS. Each WLS server instance maintains a single thread pool to be shared by all work managers defined on that WLS instance. This is different from the OC4J thread pool model in which separate thread pools are created for each configured thread pool. The self-tuning thread pool would also adjust its pool size automatically based on the throughput history that WLS gathers every 2 seconds. Thus there is no need to preset the minimum and maximum number of threads in the self-tuning thread pool.

Minimum Threads Constraint

According to the WLS documentation, the minimum threads constraint "guarantees the number of threads the server will allocate to affected requests to avoid deadlocks." It makes sure that during periods of high workloads, there would still be a certain number of threads from the self-tuning thread pool available to process work requests for all work managers that reference the minimum threads constraint.

Suppose we have a work manager called WM1 with no minimum threads constraint configured, and a new work request is scheduled for WM1 during times of high work load where all the threads in the self-tuning thread pool are already working on servicing other work requests. The new work request will be added to a queue waiting for some threads in the thread pool to be freed up before it can be processed.

Suppose WM1 is modified to reference a minimum threads constraint C1 with a value of 2, and that it is the only work manager referencing C1. If a work request for WM1 is scheduled when there are available threads in the thread pool, WLS will pick an idle thread for processing the work regardless of whether WM1 references any minimum threads constraint. However, if the work is scheduled during times of high work load and that there are no available threads in the self-tuning thread pool, WLS will first check and see how many threads in the thread pool are currently processing work requests for WM1. In this example, if there are fewer than 2 threads, WLS would create a new thread to handle the work request for WM1. However, if there are already 2 threads in the self tuning thread pool that are executing work on behalf of WM1, the new work request would be put into a queue just like in the case when there is no minimum threads constraint being configured.

It should be noted that after the work request is finished, the newly created thread will be returned to the self-tuning thread pool and would be available for processing requests from any work manger. So using the minimum threads constraint could result in increasing the number of threads in the self-tuning thread pool. This may not be desirable as it might result in degraded overall server throughput due to the additional context switching among threads, at least before the self-tuning thread pool adjusts to its optimal pool size again. Another possible side-effect of using the minimum threads constraint is that work requests that would have otherwise be put on a queue would instead be executed in a new thread. This would result in more work requests being processed for such work manager than its configured value in the fair share request class if one is configured. Thus the minimum threads constraint should be used only sparingly.

What the minimum threads constraint will not do for the work manager is that no threads will be created specifically for it in the self tuning thread pool during WLS startup. There will not be any threads set aside in the self-tuning thread pool waiting to handle work requests for WM1. Any idle threads in the self-tuning thread pool would be available to process work requests for any work manager.

Multiple work managers can reference the same minimum threads constraint, but the constraint will be applied to all the work managers that are referencing it. Suppose two work managers WM1 and WM2 both reference the same minimum threads constraint C1 with a configured value of 2. The self-tuning thread pool will create new thread for processing work requests for WM1 or WM2 only when there is no more idle threads in the thread pool and that there are fewer than 2 threads currently processing requests for either work managers. In other words, if there are already 2 threads processing work for WM1, the self-tuning thread pool will not create a new thread for the new incoming request for WM2. To have a minimum of 2 threads for each of the two work managers, each work manager would need to reference a different minimum threads constraint, each configured to have a value of 2.

The minimum threads constraint in WLS work manager is most suitable for making sure threads are available for processing work for a work manager during a period of high work load. Users should consider adding a minimum threads constraint to a work manager configuration when it is critical that progress must be made for an application even when the WLS server is under very heavy load, such as work that would have resulted in server-to-server deadlock if not being processed promptly. It is not to be used as a mean of prioritizing workload among different work managers. Users should be looking into using either the fair share request class or the response time request class for that purpose instead.

Maximum Threads Constraint

The maximum threads constraint "limits the number of concurrent threads executing requests from the constrained work set" according to the WLS documentation. It limits the number of threads in the self-tuning thread pool that can be used at any time for executing work for all work managers that references the same constraint. It is not, however, used for limiting the size of the self-tuning thread pool.

More than one work manager can reference the same maximum threads constraint, and any threads processing requests from any of these work manager would count against the limit configured in the maximum threads constraint. This is useful for limiting thread usages for related work managers.

Suppose a maximum threads constraint C2 is defined with a value of 10 and only one work manager WM3 is defined to use C2 as its maximum threads constraint. When 10 threads from the thread pool are already processing work requests for WM3, any additional work requests for WM3 will be put on a queue for C2 even if there are idle threads available in the self-tuning thread pool. The queued requests will be picked up for processing in the same order as they arrived when work requests for WM3 are completed.

Note that when the maximum threads constraint is reached, WLS may no longer able to honor the ratios as defined in the fair share or response time request classes. This is because fewer work requests will be processed for work managers with maximum threads constraint defined that would be otherwise allowed according to the configured values in the request classes. Suppose work manager WM3 in our previous example is defined with a fair share request class of value 80, while another work manager WM4 is defined with a fair share request class of value 20. During a period of sufficient load, WLS would likely process fewer requests for WM3 and have a thread-usage ratio lower than the configured 80:20 ratio between WM3 and WM4 once the maximum threads constraint for WM3 is reached.

What happens to this ratio if both WM3 and WM4 both reference the same maximum threads constraint C2? Assuming the system is under sufficient load, and that the maximum threads constraint is reached, additional requests from both WM3 and WM4 will be added to the same queue. The requests for both work managers will then be processed in the same order as they arrive and would not be according to the configured request class ratio. Thus the thread-usage ratio will not be maintained in this case either.

Like the minimum threads constraint, the maximum threads constraint is not designed as a mean to prioritize workloads among different work managers. It is most useful when there are other known limitations where a hard upper limit should be put on the number of threads that should be assigned for processing work requests, and that allocating more threads for processing the workload would not increase the overall throughput. One such example would be when the throughput of an application is limited by the size of an underlying resource pool.

Conclusion

It is important to understand how the minimum and maximum threads constrains work in WLS work managers. They are each useful in specific use cases. But for general work load balancing among various work managers, fair share and response time request classes should be used instead. Please refer to the documentation  for more information on work managers.

November 10, 2009

Developing custom MBeans to manage J2EE Applications (Part II)

This is the second part in a series of blogs, that demonstrate how to add management capability to your own application using JMX MBeans.

In Part I we did the bulk of the work. We saw:

  • How to implement a custom MBean to manage configuration associated with an application.
  • How to package the resulting code and configuration as part of the application's ear file.
  • How to register MBeans upon application startup, and unregistered them upon application stop (or undeployment).
  • How to use generic JMX clients such as JConsole to browse and edit our application's MBean.

In this second part, we will add descriptions to our:

  • MBean
  • MBean attributes
  • MBean operations
  • MBean operation parameters

We saw using JConsole, that default descriptions were generated for all of the above. However those default descriptions didn't provide any meaningful information to our MBean users. We will replace those default descriptions with custom descriptions that can be used by us human to understand the functionality provided by our MBean.

We will also add localization support to our MBean. Our goal is to ensure that our MBean's meta-data is localized based on the WebLogic Server it is deployed to. So if that server uses a French Locale, then we'd expect our MBeans descriptions to be French and not English.

The complete code sample and associated build files for part II are available as a zip file. The code has been tested against WebLogic Server 10.3.1 and JDK6. To build and deploy our sample application, please follow the instruction provided in Part I, as they also apply to part II's code and associated zip file.

Providing custom descriptions

In order to provide custom description we need to modify our MBean implementation to extend the StandardMBean class. Despite its name that class is used to implement both Standard and MXBeans.

We override the StandardMBean class many getDescription methods to provide our own custom descriptions. We use resources bundles to store our descriptions to ensure that those can be properly localized. The updated code for our MBean implementation is included below:

package blog.wls.jmx.appmbean;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;

import java.net.URL;

import java.util.Map;
import java.util.HashMap;
import java.util.Properties;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.MBeanRegistration;
import javax.management.StandardMBean;
import javax.management.MBeanInfo;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;

public class PropertyConfig extends StandardMBean implements
    PropertyConfigMXBean, MBeanRegistration {

    private String relativePath_ = null; 

    private Properties props_ = null;

    private File resource_ = null;

    private ResourceBundle resourceBundle_ = null;

    private static Map operationsParamNames_ = null;

    static {
        operationsParamNames_ = new HashMap();
        operationsParamNames_.put("setProperty", new String[] {"key", "value"});
        operationsParamNames_.put("getProperty", new String[] {"key"});
    }

    public PropertyConfig(String relativePath) throws Exception {
        super(PropertyConfigMXBean.class , true);
        props_ = new Properties();
        relativePath_ = relativePath;
    }

    public String setProperty(String key,
                              String value) throws IOException {

        String oldValue = null;

        if (value == null) {
            oldValue = String.class.cast(props_.remove(key));
        } else {
            oldValue = String.class.cast(props_.setProperty(key, value));      
        }

        save();
        return oldValue;
    }

    public String getProperty(String key) {
        return props_.getProperty(key);
    }

    public Map getProperties() {
        return (Map) props_;
    }

    private void load() throws IOException {
        
        InputStream is = new FileInputStream(resource_);
        try {
            props_.load(is);
        }
        finally {
            is.close();
        }
    } 

    private void save() throws IOException {
  
        OutputStream os = new FileOutputStream(resource_);

        try {
            props_.store(os, null);
        }
        finally {
            os.close();
        }
    }

    public ObjectName preRegister(MBeanServer server, ObjectName name)
        throws Exception {
       
        // MBean must be registered from an application thread
        // to have access to the application ClassLoader
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        URL resourceUrl = cl.getResource(relativePath_);
        resource_ = new File(resourceUrl.toURI());

        load();     

        return name;
    }

    public void postRegister(Boolean registrationDone) { }

    public void preDeregister() throws Exception {}

    public void postDeregister() {}

    private synchronized ResourceBundle getResourceBundle() {
   
        if ( resourceBundle_ == null ) {
            resourceBundle_ = 
                PropertyResourceBundle.getBundle(
                    "blog.wls.jmx.appmbean.MBeanDescriptions");
        }
       
        return resourceBundle_;
    } 

    protected String getDescription(MBeanAttributeInfo info) { 
        return getResourceBundle().getString("PropertyConfigMXBean.attribute." + 
            info.getName() ); 
    } 

    protected String getDescription(MBeanOperationInfo info) { 
        return getResourceBundle().getString("PropertyConfigMXBean.operation." + 
            info.getName() ); 
    }   

    protected String getDescription(MBeanInfo info) {
        return getResourceBundle().getString("PropertyConfigMXBean.mbean");
    }

    protected String getDescription(MBeanOperationInfo op,
                                    MBeanParameterInfo param,
                                    int sequence) {
        return getResourceBundle().getString(
            "PropertyConfigMXBean.operation." + 
            op.getName() + "." + 
            operationsParamNames_.get(op.getName())[sequence] ); 
    }

    protected String getParameterName(MBeanOperationInfo op,
                                      MBeanParameterInfo param,
                                      int sequence) {
        return operationsParamNames_.get(op.getName())[sequence];
    } 

}

Our default resource bundle class, that contains our English descriptions contains the following code:

package blog.wls.jmx.appmbean;

import java.util.ListResourceBundle;

public class MBeanDescriptions extends ListResourceBundle {
     protected Object[][] getContents() {
         return new Object[][] {
             {"PropertyConfigMXBean.mbean", 
              "MBean used to manage persistent application properties"},  
             {"PropertyConfigMXBean.attribute.Properties", 
              "Properties associated with the running application"},
             {"PropertyConfigMXBean.operation.setProperty", 
              "Create a new property, or change the value of an existing property"},
             {"PropertyConfigMXBean.operation.setProperty.key", 
              "Name that identify the property to set."},
             {"PropertyConfigMXBean.operation.setProperty.value", 
              "Value for the property being set"},
             {"PropertyConfigMXBean.operation.getProperty", 
              "Get the value for an existing property"}, 
             {"PropertyConfigMXBean.operation.getProperty.key", 
              "Name that identify the property to be retrieved"} 
        };
     }
 }

Our MBean is quite simple, and only exposes one attribute and two operations. This helps keep our resource bundle class quite small. For real world example that file will be much bigger. Note: We didn't override the getDescription method associated with our MBean constructor, to keep our sample small as this doesn't add much value to our discussion.

To add support for other languages, we only need to implement the corresponding resource bundle class. MBeanDescriptions_fr for French, and translate the description appropriately.

One interesting thing to note, is the name we used for our resource bundle keys. We didn't use arbitrary values, but we made sure we followed the following convention:

  • MBean description: <MBeanInterfaceClass>.mbean
  • MBean attribute description: <MBeanInterfaceClass>.attribute.<AttributeName>
  • MBean operation description: <MBeanInterfaceClass>.operation.<OperationName>
  • MBean operation parameter description: <MBeanInterfaceClass>.operation.<OperationName>.<ParameterName>
  • MBean constructor description: <MBeanInterfaceClass>.constructor.<ConstructorName>
  • MBean constructor parameter description: <MBeanInterfaceClass>.constructor.<ConstructorName>.<ParameterName>
We also purposely named our resource bundle class MBeanDescriptions and included it as part of the same package as our MBean. The above convention is used by the JDK 7 to localize MBean descriptions without requiring us to extend the StandardMBean class and override its many getDescription methods. Unfortunately JDK 6 doesn't support built-in JMX localization, so we have to write the above code. However we can anticipate the JDK 7 functionality (and possible early support by WebLogic) by using the above convention when specifying our MBean resource bundle and associated resource keys.

You might have noticed the following code in our updated MBean implementatrion:

    protected String getParameterName(MBeanOperationInfo op,
                                      MBeanParameterInfo param,
                                      int sequence) {
        return operationsParamNames_.get(op.getName())[sequence];
    } 
This is used to provide customized name to our operation parameters in place of the default generated 'po', 'p1', ... , 'pn' values.

The result of our hard work can be seen in the following JConsole screen shot:

app_mbean_part2_jconsole1.JPG

Consult Part I for information on how to use JConsole to browse/edit our MBean.

What's next?

What if our application is deployed to a WebLogic server running with an English Locale, and our management client wants to use a French Locale. Currently as things stand the Localization is performed based on the server Locale, and not based on the client's Locale. In the last part of this blog series, we will see how to associate a Locale with our JMX client connection, and we will update our MBean code to support client based localization.

November 9, 2009

Stateful JAX-WS with Coherence*Web


We describe a stateful JAX-WS web service with high availability provided by Coherence*Web [1]. Deployed to two separate WebLogic managed servers, the web service stores its state in http session cookies. Coherence*Web distributes and synchronizes http session data between both managed servers. As a result, two endpoints with synchronized web service state provide high availability. With one endpoint down, another could continue to serve the service request with current web service state.

Note that WebLogic clustering can also provide highly available http sessions. However, we want to demonstrate Coherence*Web as an alternative to clustering for high availability. Therefore, we do not use clustering here. Instead, we use Coherence*Web.

Setting up the stateful web service with Coherence*Web involves the following steps.

  • start Coherence*Web cache server 
  • create and configure managed servers
    • deploy Coherence*Web as a shared library to the managed servers
  • create and deploy the web service
    • stores state in http session
    • instrument the web service using Coherence*Web
    • deploy the web service to both managed servers
Start Coherence*Web Cache Server

Start Coherence*Web cache server using the command below [2].

java -server -Xms512m -Xmx512m 
-cp coherence/lib/coherence.jar:coherence/lib/coherence-web-spi.war
-Dtangosol.coherence.management.remote=true
-Dtangosol.coherence.cacheconfig=WEB-INF/classes/session-cache-config.xml
-Dtangosol.coherence.session.localstorage=true
com.tangosol.net.DefaultCacheServer

 

Create and Configure Managed Servers

Copy coherence/lib/coherence.jar [2] to $DOMAIN_HOME/lib.

Start WebLogic AdminServer using

$DOMAIN_HOME/bin/startWebLogic.sh

Create two managed servers Server-2 at port 7012 and Server-3 at port 7013 using the admin console. Start both servers using the commands below.

$DOMAIN_HOME/bin/startManagedWebLogic.sh Server-2
$DOMAIN_HOME/bin/startManagedWebLogic.sh Server-3

Deploy coherence\lib\coherence-web-api.war [2] to the servers as a shared library. 

Create and Deploy the Web Service

Install Oracle Enterprise Pack for Eclipse (OEPE) on Eclipse Galileo [4]. In Eclipse, create a web service using the following WSDL and implementation.

<definitions
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:tns="http://oracle.ws.demo/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns="http://schemas.xmlsoap.org/wsdl/"
 targetNamespace="http://oracle.ws.demo/"
 name="StatefulWSService">
<types>
<xsd:schema>
<xsd:import namespace="http://oracle.ws.demo/"
 schemaLocation="http://localhost:7001/Stateful_WS_Coherence/StatefulWSService?xsd=1" />
</xsd:schema>
</types>
<message name="addItem">
<part name="parameters" element="tns:addItem" />
</message>
<message name="addItemResponse">
<part name="parameters" element="tns:addItemResponse" />
</message>
<portType name="StatefulWS">
<operation name="addItem">
<input message="tns:addItem" />
<output message="tns:addItemResponse" />
</operation>
</portType>
<binding name="StatefulWSPortBinding" type="tns:StatefulWS">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
<operation name="addItem">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
</binding>
<service name="StatefulWSService">
<port name="StatefulWSPort" binding="tns:StatefulWSPortBinding">
<soap:address
 location="http://localhost:7001/Stateful_WS_Coherence/StatefulWSService" />
</port>
</service>
</definitions>

StatefulWS.java and Item.java

package demo.ws.oracle;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.*;
import javax.xml.ws.handler.MessageContext;
import javax.jws.*;
import javax.annotation.Resource;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*
@WebService
public class StatefulWS {
@Resource
WebServiceContext ctx;
@WebMethod()
public int addItem(String name) {
HttpServletRequest req = (HttpServletRequest)
ctx.getMessageContext().get(MessageContext.SERVLET_REQUEST);
HttpSession session = req.getSession(true);
if (session == null)
throw new WebServiceException("No HTTP Session found");
System.out.println("httpsession: id="+session.getId());

//Get the cart object from the HttpSession
List<Item> cart = (List<Item>)session.getAttribute("myCart");
if (cart == null)
{ cart = new ArrayList(); }
// Add the item to the cart
cart.add(new Item(name));
// Save the updated cart in the HTTPSession
session.setAttribute("myCart", cart);
// return the number of items in the stateful cart
return cart.size();
}
}
package demo.ws.oracle;
public class Item implements java.io.Serializable {
public String name;public Item(String name){
this.name = name;
}
}

Add coherence-web-api library into weblogic.xml [2].

<weblogic-web-app
 xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app
 http://xmlns.oracle.com/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd"
>
<weblogic-version>10.3.1</weblogic-version>
<context-root>Stateful_WS_Coherence</context-root>
<library-ref>
<library-name>coherence-web-spi</library-name>
<specification-version>1.0.0.0</specification-version>
<implementation-version>1.0.0.0</implementation-version>
<exact-match>false</exact-match>
</library-ref>
</weblogic-web-app>

Deploy the web service to both managed servers.

Test 

Run the test.

package demo.ws;
import demo.ws.oracle.*;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;

public class StatefulWSClient {
public static void main(String[] args) throws Exception {
StatefulWSService service =
new
StatefulWSService(Thread.currentThread().getContextClassLoader()
.getResource("META-INF/wsdls/StatefulWSService.wsdl")
 , new QName("http://oracle.ws.demo/", "StatefulWSService"));
StatefulWS port = service.getStatefulWSPort();

((BindingProvider)port).getRequestContext()
.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);

((BindingProvider)port).getRequestContext()
.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"http://stacd15.us.oracle.com:7012/Stateful_WS_Coherence/StatefulWSService");

System.out.println("calling " +
((BindingProvider)port).getRequestContext()
.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY));
for (int j=0; j<5; j++) {
try {
int response = port.addItem("abc");
System.out.println("Got " + response);
Thread.currentThread().sleep(3000);
} catch (Exception ex) {}
}

((BindingProvider)port).getRequestContext()
.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"http://stacd15.us.oracle.com:7013/Stateful_WS_Coherence/StatefulWSService");
System.out.println("calling " +
((BindingProvider)port).getRequestContext()
.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY));
for (int j=0; j<5; j++) {
try {
int response = port.addItem("abc");
System.out.println("Got " + response);
Thread.currentThread().sleep(3000);
} catch (Exception ex) {}
}
}
}

The test invokes the web service five times for each endpoint.  The output shows that the returned count increases, indicating synchronized web service state across both endpoints.

calling http://stacd15.us.oracle.com:7012/Stateful_WS_Coherence/StatefulWSService
Got 1
Got 2
Got 3
Got 4
Got 5
calling http://stacd15.us.oracle.com:7013/Stateful_WS_Coherence/StatefulWSService
Got 6
Got 7
Got 8
Got 9
Got 10

Both managed servers output the same http session id's.

httpsession: id=tS1DNs2XTJZuQNhNfsl4fvpuj0Qd2ZnmTmHFKtKlUX4EytukR9V0!-280396885!1257829391923
httpsession: id=tS1DNs2XTJZuQNhNfsl4fvpuj0Qd2ZnmTmHFKtKlUX4EytukR9V0!-280396885!1257829391923
httpsession: id=tS1DNs2XTJZuQNhNfs14fvpuj0Qd2ZnmTmHFKtKlUX4EytukR9V0!-280396885!1257829391923
httpsession: id=tS1DNs2XTJZuQNhNfsl4fvpuj0Qd2ZnmTmHFKtKlUX4EytukR9V0!-280396885!1257829391923

 

Reference
  1. Coherence*Web and WebLogic Server 
  2. Installing Coherence*Web on the WebLogic Server 10.3 
  3. Stateful JAX-WS 
  4. Oracle Enterprise Pack for Eclipse 
 

Continue reading "Stateful JAX-WS with Coherence*Web " »

November 2, 2009

Understanding Custom Context propagation

What is a Custom Work Context ?

The idea behind out-of-band context propagation in Weblogic server was to allow application developers to send information from a client to an application as an extra payload on the underlying transport protocol. Typical transport protocols that are supported for context propagation include Threads, JMS queues, SOAP, RMI etc. The use cases of context propagation are applications that need to carry information outside the application rather than inside the application itself. Diagnostics seems like a classic example that can ride on workcontext to propagate monitoring information from client to server and vice versa. For a quick refresher to work context propagation, please refer to latest Weblogic documentation. A few important APIs in the workcontext framework are -

1. weblogic.workarea.WorkContextMap - The main context propagation interface to tie applications with data and propagate that information via application requests. WorkContextMap is part of the client/application's JNDI environment. The map can be accessed through JNDI lookup by the name "java:comp/WorkContextMap".

2. weblogic.workarea.WorkContext - Developers could use this interface to marshal and unmarshal the user data that is passed along with the application. With the interface came a set of four implementing classes that could marshall 8 bit Ascii, Long, String and Serializable contexts.

Applications that started replying on this framework created custom contexts either by extending WorkContext or Serializable interface. We found some interesting use cases as these custom contexts were developed and put in use. The following sections document some of these use cases.

Using the correct classloader to load a custom WorkContext

Initially, the custom contexts were bundled as a part of the server through the Weblogic System classpath. As a result, the Context classloader (Weblogic System ClassLoader) for the Weblogic server could resolve these custom classes while unmarshalling the Work Context object. This scheme fell short of the fact that an application may not choose to bundle its custom context as a part of the System Loader in Weblogic. A specific application may choose to bundle the custom context as a part of the application (EAR or WAR) rather than the System classpath approach. Hence, instead of using SystemLoader.loadClass(classname) to resolve the custom context, the Server started relying on the Thread Context classloader to load the custom context. The Thread Context classloader is set correctly to the application loader if the unmarshalling of the context had to happen in an application loader scope.

Inter-operating custom work contexts across different Weblogic server versions

As Weblogic server got integrated into the Oracle Fusion Middleware Framework (11g), the context propagation framework faced a challenging problem with inter-operating custom work contexts created in 11g release with older versions of the Weblogic server. Once a newly created custom context passed from a newer version of Weblogic server to an older version, the Weblogic server had to convert the byte array into a work context object while de-serializing the context in the older server. An older version of the server would not know about newer work context object types. This resulted in ClassNotFoundExceptions.

The solution to this problem was easy. The application provider had to wrap their custom objects in a SerializableWorkContext. In effect, the SerializableWorkContext wraps a Java Serializable object (for example a custom Work Context) such that on de-serializing the work context, the server does not hit the ClassNotFoundExceptions. When the work context provider is present in the newer version of the server, it would firstly get the SerializableWorkContext from the map and then get the wrapped serializable custom object. The custom context provider knows if it can handle the new context. Hence, the serialization of the custom object gets controlled by the application provider rather than the work context framework.

Here is a snippet of code that creates a Serializable work context and puts it in the work context map for the client .

1. WorkContext serializedCustomContext = PrimitiveContextFactory.create(Serializable customContext)

2. Put the serialized custom context inside the workcontext map -

WorkContextMap map =
WorkContextHelper.getWorkContextHelper().getWorkContextMap();

map.put(MYAPP.CUSTOM_CONTEXT,serializedCustomContext);

On the receiving end, the custom context would be retrieved as -

1. WorkContextMap map =
WorkContextHelper.getWorkContextHelper().getWorkContextMap();

2. (SerializableWorkContext)WorkContext customContext = (SerializableWorkContext)map.get(MYAPP.CUSTOM_CONTEXT);

3. MyContext myCtx = (MyContext)((SerializableWorkContext)customContext).get();

Unless the application provider turned around and calls step (3) or customContext.getSerializable(), the custom object would not be converted from a byte array to an object instance. Hence, the workcontext framework would simply unmarshal the wrapped serialized object containing the payload instead of de-serializing the custom object itself. This mechanism shifts the control of de-serializing to the application provider who now has more control on when to convert the byte array into the custom work context.

If the application provider on a specific Weblogic server version cannot de-serialize the custom context, it would pass the wrapped object to a different server who may be able to read it fully. The mechanism is limited to Serializable work context types. Serialized work contexts come with a performance overhead. Hence an application provider should carefully evaluate the pros and cons of using a Serialized work context type.

So far so good. Now came the side effect of when to serialize while sending a custom context from a client.

When to serialize the custom work context before sending it from the client

There is a hidden side-effect to SerializedWorkContexts other than performance. A serialized work context is serialized the moment it is created. For example, if the client wraps its custom context in a Serializable context -

WorkContext customContext = PrimitiveContextFactory.create(Serializable context);

The custom work context is serialized at the point of creation (the create call). This is often a drawback to the application provider who may not have all the information it needs at the creation time to stuff in the custom context. Once the context is created, its too late to change it. Hence we needed a better mechanism to delay the serialization of the custom context. This allows the Serializable context data to be updated even after it is put in the WorkContextMap. From 11g release, the framework provided a new API to create a mutable Serializable object that in effect delayed the serialization of the work context -

WorkContext customContext = PrimitiveContextFactory.createMutable(Serializable context);

The mutable work context vastly improved the time window when clients could change the contents of the work context. Diagnostic frameworks often use this feature since they do not have all the information they need in the work context when creating it in the first place. However, they do have all the required information just before the request is send over the wire from the client to the server. This feature gave developers the opportunity to change the workcontext repeatedly before it was put on the wire.

We haven't talked about the inter-operability case with mutable work contexts. The Weblogic server itself needs to distinguish from a mutable (or delayed serialized work context) from the plain serialized work context. Hence the mutable flag or property had to be carried with the new type of work context so that the Server could take an appropriate action when it came across a mutable or non mutable Serializable work context.

When is a Work Context is really available to the application ?

It would be desirable for a work context send from a client to a server to be available to the application the moment the request is intercepted. However, the availability of the workcontext in the application is dependent on the client server communication protocol. For example, when a Web Services request is send over HTTP, the SOAP request is not unmarshalled by the server unless the protocol specific interceptors (or handlers who know what to do with the payload) have read the whole SOAP message. The interceptors don't fire at the first point of request interception. If applications have J2EE filters or security related policy checks that are done before the SOAP request is unmarshalled, the goodies in the workcontext payload are hidden to these consumers unless the SOAP envelope is fully unmarshalled later. This is a known limitation with how work contexts interact with the different protocols.

Unless the request is completely unmarshalled, a call to the WorkContextMap to fetch an already known work context will return null -

WorkContext context =
WorkContextHelper.getWorkContextHelper().getWorkContextMap().get(MYAPP.MY_CONTEXT);

It is therefore incumbent on frameworks who ride on Weblogic work contexts not to access the map during this window (intercept request --> unmarshall request). Though this window is small, its possible some piece of application code would like get hold of the incoming work context at the very first request interception point. Without that, they may face the dilemma of having to reconcile a work context created during this window and the real work context that reveals itself later. Such reconciliations are tough and come with even more serious inter-operability side effects. Hence, its recommended that the workcontext is only accessed from the map after the request is fully unmarshalled.

In Conclusion

Custom work contexts (mutable or un-mutable) is a handy feature for applications who intend to send extra data outside the application scope. The Weblogic server tries to hide all the underlying gory details of marshalling and unmarshalling the context associated with various protocols from the end user. The consumer simply needs to remember the "key" or "id" to the stuffed work context in the incoming request and pull the work context out of the map. Used wisely, this feature can save man hours with applications providers who intend to drive data outside their scope. The workcontext framework has also evolved over time to learn and adapt with changing needs of the application provider. Once used primarily as an internal Weblogic server feature, workcontext is now full integrated and deployed in applications running on Oracle Fusion Middleware 11g framework.

October 30, 2009

SSL Troubleshooting and Debugging

SSL Troubleshooting and Debugging

Due to the very nature of secure channel establishment, it is often difficult to even approach troubleshooting and debugging SSL related issues.

This brief article intends to illustrate the challenges, approaches and tools available for debugging these difficult scenarios.

SSL Description

Secure Socket Layer (SSL) is a protocol for providing a secure channel of communication between two computers. It makes provisions for data integrity, confidentiality and authentication. Authentication of the server - by the client - provides an assurance of the fact that the traffic has not been diverted to an attacking server. Mutual authentication requires the client to provide credentials to the server over the secure channel.

SSL Handshake Overview

In order to really be able to troubleshoot and debug SSL related issues, we need an understanding of what the protocol actually does on both the client and server sides. This understanding will enable us to quickly categorize the type of problem being encountered and hopefully a category of approaches for tracking down the root cause.

SSL Diagrams.jpg

We will touch on issues and troubleshooting approaches in the following categories:

1. Certificate Validation
2. Trust
3. Configuration

So let's briefly describe the protocol with a bit of focus on these three categories.

The client initiates the SSL connection by requesting a channel through the use of a ClientHello handshake message. This message contains the Cipher Suites that are configured to be supported by the client side and are available for the server to choose in creating the most secure channel configuration possible between the two machines. It also contains a random number to be used by the server in the generation of keys - this random number is a result of the configured or default RNG on a given platform.

The server side, in turn, responds with a ServerHello that includes the Cipher Suite selected by the server as the most appropriately secure suite for the channel. If a suitable cipher suite could not be selected from the list of supported suites provided by the client - the request for an SSL connection is denied by the server. It also includes a random number and the certificate that is to be used for authenticating the server to the client. This certificate must be validated by the client in order for it to be trusted as representing the identity asserted by it.


This validation is based on a number of possible factors (driven by configuration):

1.Whether it is expired
2.Whether it has been revoked
3.Whether it was issued by a trusted Certificate Authority
4.Whether the server name within the certificate matches the host name for the current connection

Where Things Can Go Wrong

There are a number of common scenarios that occur as a result of improperly configured environments, clients, servers and certificates that can be categorized into one or more of the afore mentioned categories.

The following are a few descriptions of these scenarios and what the approach to identifying the root cause might be.

Keystores and Truststores
- Categories: Configuration, Trust, Certificate Validation

The client (for mutual authentication) and server each present the other a certificate that represents the identity of the machine its running on. In order for either to present this certificate - it must be available within the appropriate Keystore.

Tip 1: Determine the default certificate for a machine as appropriate for your server and ensure that it exists within the configured Keystore and is available to the process that needs to present it to the corresponding partner process.

Tip 2: Ensure that the issuer of the presented certificate exists within the appropriate Truststore of the recipient process.

Supported Cipher Suites
- Categories: Configuration

As described earlier, the handshake involves the selection of the most secure Cipher Suite by the server from the list of supported suites presented by the client.

If there isn't a common Cipher Suite between the client and server, then there is no way for the two machines to establish a secure channel - as there is no common language that will be understood buy each party that provides the necessary protection offered by SSL.

Tip 3: Ensure that the appropriate Cipher Suites are enabled on the client and server sides in order to establish this common language for secure message exchange.

Tip 4: Utilize SSL debug information to determine which cipher suites has been selected
...

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <SSLTrustValidator returns: 0>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Trust status (0): NONE>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Performing hostname validation checks: stabd58>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <isMuxerActivated: false>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <17849795 SSL3/TLS MAC>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <17849795 received HANDSHAKE>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <HANDSHAKEMESSAGE: ServerHelloDone>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Ignoring not supported JCE Mac: SunJCE version 1.6 for algorithm HmacMD5>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Will use default Mac for algorithm HmacMD5>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Ignoring not supported JCE Mac: SunJCE version 1.6 for algorithm HmacSHA1>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Will use default Mac for algorithm HmacSHA1>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Will use default Mac for algorithm MD5>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Using JCE Cipher: SunJCE version 1.6 for algorithm RC4>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Ignoring not supported JCE Mac: SunJCE version 1.6 for algorithm HmacMD5>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Will use default Mac for algorithm HmacMD5>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Ignoring not supported JCE Mac: SunJCE version 1.6 for algorithm HmacSHA1>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Will use default Mac for algorithm HmacSHA1>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Using JCE Cipher: SunJCE version 1.6 for algorithm RSA>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <write HANDSHAKE, offset = 0, length = 70>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <write CHANGE_CIPHER_SPEC, offset = 0, length = 1>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Using JCE Cipher: SunJCE version 1.6 for algorithm RC4>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Ignoring not supported JCE Mac: SunJCE version 1.6 for algorithm HMACMD5>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Will use default Mac for algorithm HMACMD5>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Ignoring not supported JCE Mac: SunJCE version 1.6 for algorithm HmacMD5>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Will use default Mac for algorithm HmacMD5>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Ignoring not supported JCE Mac: SunJCE version 1.6 for algorithm HmacSHA1>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <Will use default Mac for algorithm HmacSHA1>

<Aug 19, 2009 2:34:57 PM PDT> <Debug> <SecuritySSL> <BEA-000000> <write HANDSHAKE, offset = 0, length = 16>

...


Tip 5: Utilize a tool such as SSLDump as necessary to see details of the handshake and application data message exchanges
...

11 1 0.0035 (0.0035) C>S SSLv2 compatible client hello

Version 3.1

cipher suites

TLS_RSA_WITH_RC4_128_MD5

SSL2_CK_RC4

TLS_RSA_WITH_RC4_128_SHA

TLS_DHE_DSS_WITH_RC4_128_SHA

TLS_ECDH_ECDSA_WITH_RC4_128_SHA

Unknown value 0x4e

Unknown value 0x2f

Unknown value 0x35

Unknown value 0x4b

Unknown value 0x4c

TLS_RSA_WITH_3DES_EDE_CBC_SHA

TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA

TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA

TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA

Unknown value 0x50

TLS_RSA_WITH_DES_CBC_SHA

TLS_DHE_DSS_WITH_DES_CBC_SHA

TLS_DHE_RSA_WITH_DES_CBC_SHA

TLS_ECDH_ECDSA_WITH_DES_CBC_SHA

Unknown value 0x4f

TLS_RSA_EXPORT1024_WITH_RC4_56_SHA

TLS_DHE_DSS_WITH_RC2_56_CBC_SHA

TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA

TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA

TLS_RSA_EXPORT_WITH_RC4_40_MD5

SSL2_CK_RC4_EXPORT40

TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA

TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA

TLS_RSA_EXPORT_WITH_DES40_CBC_SHA

TLS_DH_anon_WITH_3DES_EDE_CBC_SHA

TLS_DH_anon_WITH_RC4_128_MD5

TLS_DH_anon_WITH_DES_CBC_SHA

TLS_DH_anon_EXPORT_WITH_RC4_40_MD5

TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA

TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA

TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA

TLS_RSA_EXPORT_WITH_DES40_CBC_SHA

TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA

11 2 0.0053 (0.0017) S>C Handshake

ServerHello

Version 3.1

session_id[0]=


cipherSuite TLS_DH_anon_WITH_3DES_EDE_CBC_SHA

compressionMethod NULL

11 3 0.0053 (0.0000) S>C Handshake

ServerKeyExchange

Short read: 0 bytes available (expecting 2)

11 4 0.0065 (0.0012) S>C Handshake

ServerHelloDone

11 5 0.0976 (0.0910) C>S Handshake

ClientKeyExchange

DiffieHellmanClientPublicValue[128]=

8a 23 78 02 8a a5 fc 03 f4 9b 7c 33 05 22 36 91

85 9d 17 e4 bf bf 0a 3e be 45 25 47 07 e0 9c a2

e5 d6 bf 78 95 f1 84 ca cb cc e4 3e f3 d8 d4 9a

3a 01 71 5c 29 0c 0b f9 69 8d 3e a6 f4 08 f0 36

18 fd a7 b9 3e 30 4e a4 a6 19 d9 d3 64 1c 3c 78

d3 c3 fa 83 07 58 f2 be d2 32 80 c0 32 4e 49 4c

bf 73 1a f2 d8 fd f2 16 c7 31 da 48 58 50 bb 99

3f a4 8c 31 6e 5f ed e8 0d d8 91 cf 8f eb fa d8

11 6 0.0976 (0.0000) C>S ChangeCipherSpec

11 7 0.0976 (0.0000) C>S Handshake

11 8 0.0997 (0.0021) S>C ChangeCipherSpec

11 9 0.1000 (0.0002) S>C Handshake

11 10 0.3580 (0.2580) C>S application_data

11 11 0.3580 (0.0000) C>S application_data

11 12 0.3586 (0.0005) S>C application_data

11 13 2.5039 (2.1453) C>S application_data

11 14 2.5039 (0.0000) C>S application_data

11 15 2.5053 (0.0013) S>C application_data

8 20 31.4483 (3.3621) C>S application_data

8 21 31.4483 (0.0000) C>S application_data

8 22 31.4507 (0.0024) S>C application_data

8 23 31.4508 (0.0000) S>C application_data

8 24 32.0824 (0.6316) C>S application_data

8 25 32.0824 (0.0000) C>S application_data

8 26 32.2550 (0.1726) S>C application_data

8 27 32.2550 (0.0000) S>C application_data

8 28 33.1710 (0.9159) C>S application_data

8 29 33.1710 (0.0000) C>S application_data

8 30 33.1745 (0.0035) S>C application_data

8 31 33.1754 (0.0009) C>S application_data

...

Anonymous Cipher Suite
- Categories: Configuration

The failure of a client or server to reject a certificate that is not trusted may present as potential SSL problem. Recall earlier that I describe the process of selecting the most secure Cipher Suite common between both parties.

In a scenario where one of the parties has only the anonymous Cipher Suite enabled and the other party also has it enabled - even if it is one of many - the anonymous cipher suite will be selected and the connection will not be rejected.

Tip 6: see Tip 5 above - in fact, the example ssldump output above is from troubleshooting just such a scenario

Trusted CA's

- Categories: Trust, Configuration

Unless the issuer of a certificate is found in the Truststore of a client or server involved in the establishment of an SSL connection, the certificate validation will fail.

Tip 7: Determine the Truststore/s in use and whether or not the issuer of the presented certificate exists within the configured Truststore

Tip 8: Utilize keytool in order to dump the contents of the Truststores (or keystores for the presented certificates)

Alias name: ttelesecglobalrootclass3ca
Creation date: Feb 10, 2009
Entry type: trustedCertEntry

Owner: CN=T-TeleSec GlobalRoot Class 3, OU=T-Systems Trust Center, O=T-Systems Enterprise Services GmbH, C=DE
Issuer: CN=T-TeleSec GlobalRoot Class 3, OU=T-Systems Trust Center, O=T-Systems Enterprise Services GmbH, C=DE
Serial number: 1
Valid from: Wed Oct 01 03:29:56 PDT 2008 until: Sat Oct 01 16:59:59 PDT 2033
Certificate fingerprints:
MD5: CA:FB:40:A8:4E:39:92:8A:1D:FE:8E:2F:C4:27:EA:EF
SHA1: 55:A6:72:3E:CB:F2:EC:CD:C3:23:74:70:19:9D:2A:BE:11:E3:81:D1
Signature algorithm name: SHA256withRSA
Version: 3

Extensions:

#1: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
Key_CertSign
Crl_Sign
]

#2: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:2147483647
]

#3: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: B5 03 F7 76 3B 61 82 6A 12 AA 18 53 EB 03 21 94 ...v;a.j...S..!.
0010: BF FE CE CA ....
]
]


Certificate Expiration
- Categories: Certificate Validation, Configuration

Tip 9: see Tip 8 above - In the example keytool output above you can see the dates for which the particular certificate is valid.

Valid from: Wed Oct 01 03:29:56 PDT 2008 until: Sat Oct 01 16:59:59 PDT 2033

Random Number Generation (RNG) Issue
- Categories: Configuration

Performance issues may be encountered due to low or zero entropy on a server. This entropy results in longer than expected blocking in acquiring the random number seeding from /dev/random. There are a couple potential workarounds.

1.use /dev/urandom - NOTE: this may result in degenerated encryption strength and must be investigated by your system/security administrators
2.patches may be available for your particular Linux flavor or Solaris

Tip 10: Observe through SSL debug output whether or not the handshake is timing out as this is an indicator that perhaps we are blocking on the RNG

Tip 11: Ensure that all related patches have been installed on your machine.

Available Tools and Facts

Like any other specialization, troubleshooting and debugging security - and SSL in particular - presents unique challenges and to address these unique challenges we need to be prepared by having appropriate tools and facts at our disposal.

In order to be productive in this area, we need to know certain things about the environment, management consoles, etc.


Debug output
Each middleware platform provides the ability to configure the server to run with SSL debug logging turned on. This configuration enables the viewer of the logs to see pertinent information regarding the configuration and runtime behavior of the handshaking and application data message exchanges in real time.

Tip 12: Determine what the configuration mechanism is for turning on SSL debug information on your platform. On WebLogic Server the following System Properties are used to configure SSL debug information and can be used on the command line or within start up scripts:

-Dssl.debug=true -Dweblogic.StdoutDebugEnabled=true -Dweblogic.security.SSL.verbose=true

Tip 13: When you have access to starting the server with these System properties do so immediately - the information that it creates will be valuable - if not to you then to someone else that is pulled in to help debug - at which time you will be request to do so anyway and go through the whole thing again.

SSLDump

In cases where we don't have access to the server to restart with SSL debug logging or when we would like to supplement that output with additional information SSLDump is hugely valuable.

ssldump is an SSLv3/TLS network protocol analyzer. It identifies TCP connections on the chosen network interface and attempts to interpret them as SSLv3/TLS traffic. When it identifies SSLv3/TLS traffic, it decodes the records and displays them in a textual form to stdout. If provided with the appropriate keying material, it will also decrypt the connections and display the application data traffic.

Tip 14: Download a copy of ssldump from http://www.rtfm.com/ssldump/. You will need to build it on your platform and you may actually need to resolve a couple compilation errors - but it is well worth it.

Examples:

To listen to traffic on port 443:
ssldump -i eth0 port 443

To listen to traffic to the server target on port 443:
ssldump -i eth0 port 443 and host target

To decrypt traffic to the host target server.pem and the password foobar:
ssldump -i eth0 -Ad -k ~/server.pem -p foobar host target

Generic SSL Client

Once you have ssldump built and running on your machine, you can use any SSL client to target the server that you are trying to troubleshoot. Often a browser will suffice - however you may need to build a client more appropriate for your usecase.

Tip 15: Utilize ssldump, SSL debug logging and your SSL client to observe the messages exchanged and the runtime behavior that manifests as a result of your current configuration.

Platform Specific Knowledge

Become intimately familiar with where the appropriate keystores, truststores, configuration files and management consoles are located.

Tip 16: Maintain a checklist of this information and keep it handy so that you don't have to rediscover it every time you encounter SSL issues.

Books

SSL and TLS: Designing and Building Secure Systems, Addison-Wesley, 2001 ISBN 0-201-61598-3

http://www.rtfm.com/sslbook/

Use WLST to configure AQ JMS in Weblogic

For AQ JMS Weblogic integration, our documents were based on Weblogic admin console. While the admin console is more user friendly because of its intuitive and easy GUI interface, it is less desirable for some situations such as following comparing with WLST.

1) The configuration is huge with hundreds of connection factories and destinations
2) The same configuration need to be done repeatedly many times
3) The User wants to keep an easy to read record of what have been configured

The difficulty of directly using the WLST is to understand and navigate the complex structure of WebLogic management. In this blog, we demonstrate a jython script to help to overcome this difficulty.

The script is structured into several jython functions so it can be easily customized by other people. Here is the list of functions

def createDataSource(host, port, sid, username='', password='', \
dsName=DEFAULT_DATASOURCE_NAME, \
dsJndiName=DEFAULT_DATASOURCE_JNDI, xa=DEFAULT_DATASOURCE_XA, \
serverTargets=DEFAULT_DATASOURCE_SERVER_TARGETS, \
clusterTargets=DEFAULT_DATASOURCE_CLUSTER_TARGETS):

This function creates a Weblogic datasource for AQJMS usage

host: the host name of the Oracle database
port: the port number of the Oracle database
sid: the SID of the Oracle database
username: the Oracle database username
password: the Oracle database password
dsName: the name of the datasource, the default is 'aqds'
dsJndiName: the JNDI name of the datasource, the default is 'jdbc/aqds'
xa: whether to use the XA driver or non-XA driver
serverTargets: the server targets of this datasource in a comma separated string
the default is 'AdminServer'
clusterTargets: the cluster targets of this datasource in a comma separated string
the default is empty string

This function will ask for Oracle database username and password dynamically
if they are not passed by parameters

def createAQModule(aqcfs, aqdests, \
moduleName=DEFAULT_AQ_MODULE_NAME, \
fsName=DEFAULT_AQ_FS_NAME, \
dsJndiName=DEFAULT_DATASOURCE_JNDI, \
serverTargets=DEFAULT_AQ_MODULE_SERVER_TARGETS,\
clusterTargets=DEFAULT_AQ_MODULE_CLUSTER_TARGETS):

This function creates an AQ module in Weblogic Server
aqcfs: A sequence of connection factories data of this AQ foreign server
aqdests: A sequence of destinations data of this AQ foreign server
moduleName: the name of this AQ module, the default is 'aq_module'
fsName: the name of the AQ foreign server, the default is 'aq_fs'
dsJndiName: the JNDI name of the Weblogic datasource of this AQ foreign server,
the default is 'jdbc/aqds'
serverTargets: the server targets of this AQ module in a comma separated string
the default is ''AdminServer'
clusterTargets: the cluster targets of this AQ module in a comma separated string
the default is empty string

def setupAQJMSFromFile(filepath):

This function parses the configuration file and setup AQ module and datasource in
Weblogic with the data in the configuration file. The configuration file is in key=value
format.

The list of mandatory keys are listed below
HOST: the host of AQ server
PORT: the port of AQ server
SID: the database sid of AQ server
USERNAME: the database username of AQ server
PASSWORD: the database password of AQ server

The list of optional keys are listed below
CONN_FACT: the data to create a Weblogic foreign server connection factory
pointing to an AQ JMS connection factory. The value of this property is as
following:
{WLS_CF_NAME:,CF_JNDI:,CF_TYPE:}
where the CF_TYPE has to be one of 'ConnectionFactory',
'QueueConnectionFactory', 'TopicConnectionFactory', 'XAConnectionFactory'
'XAQueueConnectionFactory' and 'XATopicConnectionFactory'
DESTINATION: the data to create a Webloic foreign server destination pointing to an
AQ destination. The value of this property is as following:
{WLS_DEST_NAME:,AQ_DEST_NAME:,DEST_JNDI:
,DEST_TYPE:}
where the DEST_TYPE has to be either 'QUEUE' or 'TOPIC'
DATASOURCE_NAME: the name of the datasource, default to 'aqds'
DATASOURCE_JNDI: the JNDI name of the datasource, default to 'jdbc/aqds'
DATASOURCE_XA: whether the datasource uses XA driver, default to 1
DATASOURCE_SERVER_TARGETS: the server targets of this datasource in a
comma separated string. default to 'AdminServer'
DATASOURCE_CLUSTER_TARGETS: the cluster targets of this datasource in a
comma separated string. default to empty string
AQ_MODULE_NAME: the Weblogic module for AQJMS, default to 'aq_module'
AQ_FS_NAME: the Weblogic foreign server name for AQJMS, default to 'aq_fs'
AQ_MODULE_SERVER_TARGETS: the server targets of this aq module in a comma
separated string. default to 'AdminServer'
AQ_MODULE_CLUSTER_TARGETS: the cluster targets of this aq module in a
comma separated string. default to empty string


When the script is run, it will ask the user for a configuration file and call 'setupAQJMSFromFile' with this file. Most configuration parameters in this script have default value except the most essential ones such as database host, port, sid, connection factory info and destination info. The script also does a best effort validation of these data.


Here are some examples of using this script.

1) Running the script standalone

java weblogic.WLST aq_wlst.py

with the AQ configuration file as following:
HOST=localhost
PORT=5521
SID=db11
USERNAME=jmsuser
PASSWORD=jmsuser
CONN_FACT=WLS_CF_NAME:aqcf,CF_JNDI:aqcf,CF_TYPE:XAQueueConnectionFactory}
DESTINATION=WLS_DEST_NAME:aq_queue,AQ_DEST_NAME:MY_QUEUE,DEST_JNDI:aq_queue,DEST_TYPE:QUEUE}
DATASOURCE_SERVER_TARGETS=ms1,ms2
DATASOURCE_CLUSTER_TARGETS=Cluster-0
AQ_MODULE_SERVER_TARGETS=ms1,ms2
AQ_MODULE_CLUSTER_TARGETS=Cluster-0

After it runs, the datasource, AQ module, AQ foreign server, connection factories and destinations will be configured and targeted. The datasource name, datasource JNDI name, AQ module name, AQ foreign server name are not provided in the configuration file, so the script uses the default value as above.

2) Running the script to execute multiple AQ configuration files in one transaction

The script is structured into several functions so it is easy to customize or extend. For example, here is an example extension. We can modify the main function of the script to get all the files with the extension conf in the current directory and execute them in one transaction instead of asking for configuration file from the user.


import glob
connect()
failed=1
try:
edit()
startEdit()
for cf in glob.glob("*.conf"):
setupAQJMSFromFile(cf)
activate(200000, block='true')
failed=0
finally:
if failed:
undo('false','y')
disconnect('true')

The script and a sample configuration file are attached with this blog.

aq_wlst.py

myconfig