Simple Introduction to using the Enterprise Manager SOA/BPM Facade API

Overview

There may be times when you need to expose just a small section of what is displayed in the Enterprise Manager console for SOA/BPM (EM console).

A simple example can be where stakeholders on the systems integration or customer teams want to monitor a dashboard of statistics on how many instances of a composite have been created and how many have faulted.

You can see this in the EM, as shown below

DeployedCompositeStatsInEM

Some of these stakeholders may not have knowledge of  EM console and they just want a quick view into the statistics, without having to navigate EM.

This post describes how to use the Oracle Fusion Middleware Infrastructure Management Java API  for Oracle SOA Suite (also called the Facade API)  to build a custom ADF page to display this information. If you want a quick introduction in using the Facade API, this post is for you.

To keep it extremely simple, I am going to create a single page which displays the same information as in the EM/soa-infra/Deployed Composites page. I don’t want the user to have to sign in, because this  runs in an internal network  and that way they can bookmark it and we don’t have to create a user id for each of these users.

This is how I want it to look, yes, ugly…but…quick and has the utility value and serves its purpose.

CompositeStats

Coding the Model

Here’s the code I use to retrieve the statistics for Composites. I’m using the EM API. You can find it at EM Facade API Reference

    public List getCompositeStats() {
        
        try {
            Hashtable<String, String> jndiProps = new Hashtable<String, String>();
            jndiProps.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
            jndiProps.put(Context.PROVIDER_URL, "t3://localhost:8001");
            /*
            jndiProps.put(Context.SECURITY_PRINCIPAL, principal);
            jndiProps.put(Context.SECURITY_CREDENTIALS, creds);
            */
            Locator locator = LocatorFactory.createLocator(jndiProps);
            //Locator locator = LocatorFactory.createLocator();

            CompositeFilter filter = new CompositeFilter();
            CompositeInstanceFilter instanceFilter =
                new CompositeInstanceFilter();

            List composites = locator.getComposites(filter);
           
            List result =
                new ArrayList();

            for (Composite composite : composites) {
                CompositeStatsView view = new CompositeStatsView();
                view.setName(composite.getCompositeDN().toString());
                instanceFilter.setCompositeDN(composite.getCompositeDN());
                view.setInstances(locator.getNumberOfCompositeInstances(instanceFilter));
                view.setFaults(locator.getNumberOfFaultedCompositeInstances(composite.getCompositeDN().toString()));
                result.add(view);
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

The oracle.soa.management.facade.Locator is the main entry point for the Facade API. It allows us to search for component instances, components and composites and do lifecycle operations. The CompositeStatsView you see in the code snippet is just a custom javabean I wrote to pass back some data to the UI layer.

You can see that I get an instance of the Locator using the LocatorFactory.createLocator (jndiProps). I’m passing in JNDI properties which helps the LocatorFactory create the Locator from the soa server JNDI. I’m planning to run my app on a separate server, which is why I need to pass this in. If you plan to run this on soa server , you can use the other createLocator () method on LocatorFactory, where you don’t have to pass in the JNDI properties.

What about Security?

Assume you create a data-control from a class that contains the code above and then drop the getCompositeStats method shown above as a read-only table (If you are absolutely new to ADF and want to see how I did this, I will soon be adding associated links to show how I did that).

When you run the ADF page you will see an issue. It doesn’t return any composite statistics. You may see a page like this, when you try to execute the query.

NoPermission

This is because you need the Admin role to execute these methods in the API. If you look at the model code closely, I have commented the following lines

/*
jndiProps.put(Context.SECURITY_PRINCIPAL, principal);
jndiProps.put(Context.SECURITY_CREDENTIALS, creds);
*/

Why did I comment these? Wouldn’t everything have worked just fine if I kept those in and  connected as ‘weblogic’ or whichever user has the Admin role. The answer of course is , yes, it would have worked. But I didn’t do it because its impractical. In a real deployment environment, I’m not going to know the user id/ password of a user with the Admin role.

One way to deal with this is to have this code run as a user with the Admin role. To do this, I have wrapped this code in an Stateless Session EJB instead of a POJO. In an EJB, I can use the @RunAs annotation to have the code behave as if it is being run by an Admin.

Here’s how my EJB code looks

package com.fusionapplied.compositestatsapp.model;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import javax.annotation.security.DeclareRoles;
import javax.annotation.security.RunAs;

import javax.ejb.Stateless;

import javax.naming.Context;
import javax.naming.InitialContext;

import oracle.soa.management.facade.Composite;
import oracle.soa.management.facade.Locator;
import oracle.soa.management.facade.LocatorFactory;
import oracle.soa.management.util.CompositeFilter;
import oracle.soa.management.util.CompositeInstanceFilter;

@Stateless(name = "CompositeStatsServiceSessionEJB", mappedName = "CompositeStatsApp-Model-CompositeStatsServiceSessionEJB")
@DeclareRoles( { "admin" } )
@RunAs ("admin")
public class CompositeStatsServiceSessionEJBBean implements CompositeStatsServiceSessionEJB,
                                                            CompositeStatsServiceSessionEJBLocal {
    public CompositeStatsServiceSessionEJBBean() {
    }

    public List getCompositeStats() {
        
        try {
            Hashtable<String, String> jndiProps = new Hashtable<String, String>();
            jndiProps.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
            jndiProps.put(Context.PROVIDER_URL, "t3://localhost:8001");
            /*
            jndiProps.put(Context.SECURITY_PRINCIPAL, principal);
            jndiProps.put(Context.SECURITY_CREDENTIALS, creds);
            */
            Locator locator = LocatorFactory.createLocator(jndiProps);
            //Locator locator = LocatorFactory.createLocator();

            CompositeFilter filter = new CompositeFilter();
            CompositeInstanceFilter instanceFilter =
                new CompositeInstanceFilter();

            List composites = locator.getComposites(filter);
            
            List result =
                new ArrayList();

            for (Composite composite : composites) {
                CompositeStatsView view = new CompositeStatsView();
                view.setName(composite.getCompositeDN().toString());
                instanceFilter.setCompositeDN(composite.getCompositeDN());
                view.setInstances(locator.getNumberOfCompositeInstances(instanceFilter));
                view.setFaults(locator.getNumberOfFaultedCompositeInstances(composite.getCompositeDN().toString()));
                result.add(view);
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

Notice that I have a @RunAs (“admin”) , where “admin” is custom role I made up. All I have to do now is to map the “weblogic” user (or any user with the Admin role) to the “admin” role in  the weblogic-ejb-jar.xml

Here’s how my weblogic-ejb-jar.xml looks

<?xml version = '1.0' encoding = 'windows-1252'?>
<weblogic-ejb-jar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-ejb-jar http://www.bea.com/ns/weblogic/weblogic-ejb-jar/1.0/weblogic-ejb-jar.xsd"
                  xmlns="http://www.bea.com/ns/weblogic/weblogic-ejb-jar">
                  
  <run-as-role-assignment>
    <role-name>admin</role-name>
    <run-as-principal-name>weblogic</run-as-principal-name>
  </run-as-role-assignment>
  
</weblogic-ejb-jar>

Once I had the EJB in place, I created a data-control from it and dropped the getCompositeStats as an ADF Readonly Table in the JSP to display the information.

This works when I know about a user id with Admin role ( like “weblogic”) . What do I do when I don’t know which user id has an Admin role (for example in a production environment where I may not have that kind of visibility).  What should I do if I want to make it configurable so that the user id can be changed as per the deployment environment?

Using a Deployment Plan

You can use a deployment plan to map the user id as per the environment into the weblogic-ejb-jar.xml. I have shown what my deployment plan looks like below. I use this deployment plan with the ear file I have for my application. The ear file includes the ejb module and also the web module.

<?xml version='1.0' encoding='UTF-8'?>
<deployment-plan xmlns="http://xmlns.oracle.com/weblogic/deployment-plan" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/deployment-plan http://xmlns.oracle.com/weblogic/deployment-plan/1.0/deployment-plan.xsd">
  <application-name>CompositeStatsApp</application-name>
  <variable-definition>
    <variable>
      <name>SessionDescriptor_timeoutSecs_13931025589320</name>
      <value>3601</value>
    </variable>
    <variable>
      <name>var-run-as-principal-name</name>
      <value>weblogic</value>
    </variable> 
  </variable-definition>
  <module-override>
    <module-name>CompositeStatsApp.ear</module-name>
    <module-type>ear</module-type>
    <module-descriptor external="false">
      <root-element>weblogic-application</root-element>
      <uri>META-INF/weblogic-application.xml</uri>
      <variable-assignment>
        <name>SessionDescriptor_timeoutSecs_13931025589320</name>
        <xpath>/weblogic-application/session-descriptor/timeout-secs</xpath>
      </variable-assignment>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>application</root-element>
      <uri>META-INF/application.xml</uri>
    </module-descriptor>
    <module-descriptor external="true">
      <root-element>wldf-resource</root-element>
      <uri>META-INF/weblogic-diagnostics.xml</uri>
    </module-descriptor>
  </module-override>
  <module-override>
    <module-name>CompositeStatsApp_Model_ejb1.jar</module-name>
    <module-type>ejb</module-type>
    <module-descriptor external="false">
      <root-element>weblogic-ejb-jar</root-element>
      <uri>META-INF/weblogic-ejb-jar.xml</uri>
      <variable-assignment>
        <name>var-run-as-principal-name</name>
        <xpath>/weblogic-ejb-jar/run-as-role-assignment/[role-name="admin"]/run-as-principal-name</xpath>
      </variable-assignment>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>ejb-jar</root-element>
      <uri>META-INF/ejb-jar.xml</uri>
    </module-descriptor>
  </module-override>
  <module-override>
    <module-name>CompositeStatsApp_ViewController_webapp1.war</module-name>
    <module-type>war</module-type>
    <module-descriptor external="false">
      <root-element>weblogic-web-app</root-element>
      <uri>WEB-INF/weblogic.xml</uri>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>web-app</root-element>
      <uri>WEB-INF/web.xml</uri>
    </module-descriptor>
  </module-override>
  <config-root>C:\JDeveloper\mywork\CompositeStatsApp\deploy\plan</config-root>
</deployment-plan>

I have created a variable called var-run-as-principal-name and assigned it a value of “weblogic”. This should be whatever user has the Admin role.

    <variable>
      <name>var-run-as-principal-name</name>
      <value>weblogic</value>
    </variable> 

and here’s the section that maps this user id to the “admin” role for my ejb.

  <module-override>
    <module-name>CompositeStatsApp_Model_ejb1.jar</module-name>
    <module-type>ejb</module-type>
    <module-descriptor external="false">
      <root-element>weblogic-ejb-jar</root-element>
      <uri>META-INF/weblogic-ejb-jar.xml</uri>
      <variable-assignment>
        <name>var-run-as-principal-name</name>
        <xpath>/weblogic-ejb-jar/run-as-role-assignment/[role-name="admin"]/run-as-principal-name</xpath>
      </variable-assignment>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>ejb-jar</root-element>
      <uri>META-INF/ejb-jar.xml</uri>
    </module-descriptor>
  </module-override>

When I deploy my ear file with this deployment plan the methods execute successfully without any issue around permissions and I can see my Composite information.

CompositeStats

References

EM Facade API Reference

Programmatically Managing SOA Composite Applications with the Facade API

Understanding WebLogic Deployment Plan Contents