Monday, October 20, 2014

Oracle Service Bus: Obtaining a list of exposed SOAP HTTP endpoints

The Oracle Service Bus is often used for service virtualization. Endpoints are exposed on the Service Bus which proxy other services. Using such an abstraction layer can provide benefits such as (among many other things) monitoring/logging, dealing with different versions of services, throttling/error handling and result caching.

In this blog I will provide a small (Java) script, which works for SOA Suite 11g and 12c, which determines exposed endpoints on the Service Bus.


How to determine endpoints?

In order to determine endpoints on the Service Bus, The Service Bus MBeans can be accessed. These MBeans can obtained from within a local context inside the Service Bus or remotely via JMX (when configured, see http://stackoverflow.com/questions/1013916/how-to-enable-jmx-on-weblogic-10-x). In this example I'll use a remote connection to a Weblogic Server instance which runs on the same machine (JDeveloper IntegratedWeblogicServer). To browse MBeans, you can use jvisualvm (http://docs.oracle.com/javase/7/docs/technotes/guides/visualvm/) which is distributed as part of the Oracle JDK. JVisualVM has a plugin to browse MBeans.



When connected, the Service Bus MBeans are located under com.oracle.osb. The proxy services which define the exposed endpoints, can be recognized by the Proxy$ prefix. In order to determine the actual endpoint, you can look at the ResourceConfigurationMBean of the proxy service. Under configuration, transport-configuration you can find a property called url. The script also filters HTTP SOAP services since the url field is also used for other transports. A replace of // with / is done on the combination server:host/url since the url can start with a /. This causes no difference in functioning but provides better readable output. If you want WSDL's, you can add '?wsdl' to the obtained endpoint.

The script requires some libraries. For 11g, you can look at http://techrambler99.blogspot.nl/2013/11/oracle-service-bus-using-management.html. On that blog, more specific API calls are done which have more dependencies. This example requires less. For 12c, it was enough to add the Weblogic 12.1 remote client library in the project properties.


The script does not determine the hostname/port of the server the Service Bus is currently running on. This could lead to misinterpretation when for example a load-balancer or another proxy component (such as OHS) is used.

The script

The username/host/port/password are hardcoded. You might want to move that to a property file or provide them using a command-line. The CompositeDataSupport class which is used, is quite useful in that you can quickly determine nested properties.

 import java.io.IOException;  
 import java.net.MalformedURLException;  
 import java.util.ArrayList;  
 import java.util.Hashtable;  
 import java.util.Set;  
 import javax.management.MBeanServer;  
 import javax.management.MBeanServerConnection;  
 import javax.management.ObjectName;  
 import javax.management.openmbean.CompositeDataSupport;  
 import javax.management.remote.JMXConnector;  
 import javax.management.remote.JMXConnectorFactory;  
 import javax.management.remote.JMXServiceURL;  
 import javax.naming.Context;  
 import javax.naming.InitialContext;  
 import javax.naming.NamingException;  
 import weblogic.management.mbeanservers.domainruntime.DomainRuntimeServiceMBean;  
 public class SBEndpointList {  
   private static MBeanServerConnection connection;  
   private static String HOST = "localhost";  
   private static Integer PORT = 7101;  
   private static String USERNAME = "weblogic";  
   private static String PASSWORD = "Welcome01";  
   public SBEndpointList() throws IOException, MalformedURLException, NamingException {  
     this.connection = this.getMBeanServerConnection();  
   }  
   private ObjectName[] getObjectNames(ObjectName base, String child) throws Exception {  
     return (ObjectName[]) connection.getAttribute(base, child);  
   }  
   public ArrayList<String> getEndpoints() throws Exception {  
     ArrayList<String> result = new ArrayList<String>();  
     Set<ObjectName> osbconfigs =  
       connection.queryNames(new ObjectName("com.oracle.osb:Type=ResourceConfigurationMBean,*"), null);  
     for (ObjectName config : osbconfigs) {  
       if (config.getKeyProperty("Name").startsWith("ProxyService$")) {  
         //System.out.println(config);  
         CompositeDataSupport cds = (CompositeDataSupport) connection.getAttribute(config, "Configuration");  
         String servicetype = (String) cds.get("service-type");  
         if (servicetype.equals("SOAP")) {  
           CompositeDataSupport pstt = (CompositeDataSupport) cds.get("transport-configuration");  
           String url = (String) pstt.get("url");  
           String tt = (String) pstt.get("transport-type");  
           if (tt.equals("http")) {  
             result.add("http://SERVER:PORT" +("/" + url).replace("//" ,"/") );  
           }  
         }  
       }  
     }  
     return result;  
   }  
   private JMXConnector initRemoteConnection(String hostname, int port, String username,  
                        String password) throws IOException, MalformedURLException {  
     JMXServiceURL serviceURL =  
       new JMXServiceURL("t3", hostname, port, "/jndi/" + DomainRuntimeServiceMBean.MBEANSERVER_JNDI_NAME);  
     Hashtable<String, String> h = new Hashtable<String, String>();  
     h.put(Context.SECURITY_PRINCIPAL, username);  
     h.put(Context.SECURITY_CREDENTIALS, password);  
     h.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, "weblogic.management.remote");  
     return JMXConnectorFactory.connect(serviceURL, h);  
   }  
   private MBeanServerConnection getMBeanServerConnection() throws IOException, MalformedURLException,  
                                   NamingException {  
     try {  
       InitialContext ctx = new InitialContext();  
       MBeanServer server = (MBeanServer) ctx.lookup("java:comp/env/jmx/runtime");  
       return server;  
     } catch (Exception e) {  
       JMXConnector jmxcon = initRemoteConnection(HOST, PORT, USERNAME, PASSWORD);  
       return jmxcon.getMBeanServerConnection();  
     }  
   }  
   public static void main(String[] args) throws IOException, MalformedURLException, Exception {  
     SBEndpointList me = new SBEndpointList();  
     ArrayList<String> result = me.getEndpoints();  
     System.out.println("Endpoints:");  
     for (String endpoint : result) {  
       System.out.println(endpoint);  
     }  
   }  
 }