Oracle SOA-Suite : vérifier la configuration des composites

Bien souvent, les composites que l’on déploie se voient appliqué un plan de configuration au moment du déploiement.

Malgré tous les efforts que l’on réalise pour avoir une qualité parfaite (tests, vérification de la configuration…), beaucoup doivent connaitre la sensation de la petite goutte de sueur perlant sur le front en se disant intérieurement « mince… j’espères que je n’ai pas oublié une configuration ».

Ici nous allons présenter un petit use-case qui vous permettra de vérifier facilement toutes vos configurations qui sont déployées !

Où trouver les configurations utilisées

Lorsqu’un composite est déployé sur le serveur, il existe plusieurs moyens d’accéder à leurs configurations. Ici nous allons utiliser :

  • Les System MBeans
  • La SOA Facade API

 Ce choix vient plutôt de l’aspect pratique : la SOA-API n’offre pas tout, mais reste plus simple à utiliser que les MBeans quand c’est possible.

Cet exemple ne sera donc qu’une des applications pour lesquelles ces outils peuvent être utile.

 Quelles configurations vérifier ?
Dans notre exemple, nous avons allons vérifier plusieurs éléments :

  • Les imports utilisés par le composite
  • Les endpoints utilisés par les références de type web-service
  • Les datasources utilisées par nos références de type JCA

Ces éléments ne représentent pas forcément tous les éléments vérifiables, mais ils seront un bon début pour vous assurer que vos configurations sont bonnes.

Comment construire l’outil de vérification

Nous n’allons pas ici présenter l’ensemble des classes utilisées pour réaliser la vérification. Les appels aux MBeans / Facade API seront présentés, les traitements de vérification, eux,  seront uniquement abordés de manière informelle. Cependant vous aurez toutes les billes en main pour mettre en place un batch complet.

Le code complet est disponible en  fin d’article

Tout d’abord, il vous faudra préparer les connexions pour utiliser les System MBeans et la SOA Facade API. Pour cela, reportez-vous aux articles présentant ces outils pour savoir comment effectuer les connexions, et les dépendances nécessaires.

Ensuite nous allons tout simplement lister les composites (récupération par MBean). On notera dans la chaine d’interrogation l’utilisation d’ « * » pour prendre n’importe quelle valeur. On pourrait limiter notre recherche à une partition ou à un composite particulier si on le souhaitait.

String initialMBeanName = "oracle.soa.config:partition=*,j2eeType=SCAComposite,revision=*,label=*,Application=*,wsconfigtype=WebServicesConfig,name=*";
Set<ObjectName> compositesMBeansNames = mbeanServerConnection.queryNames(new ObjectName(initialMBeanName), null);

Nous itérons sur les mbean de composites obtenus pour effectuer toutes les vérification sur le composite en question.

En premier, les imports. On utilise pour cela l’API Facade, n’ayant pas retrouvé cette information par MBean :

String partition = extractCompositePartition(compositeMBeanName);
String compositeName = extractCompositeName(compositeMBeanName);
String revision = extractCompositeRevision(compositeMBeanName);
String compositeDN = partition + "/" + compositeName + "!" + revision;
System.out.println("Composite : " + compositeDN);
CompositeFilter filter = new CompositeFilter();
filter.setCompositeDN(compositeDN);
List<Composite> composites = locator.getComposites(filter);
Composite composite = composites.get(0);
List<ImportInfo> importsinfo = composite.getImports();
if (importsinfo != null)
{
  for (ImportInfo importinfo : importsinfo)
  {
    // méthode implémentant le contrôle souhaité sur l’import
    checkImport(importinfo);
  }
}

Ensuite, nous allons récupérer la liste des références utilisés par le composite.

Pour chacune de ces références, nous récupérons ses bindings.

Selon son type, nous allons vérifier soit la datasource utilisée, soit le endpoint utilisé.

Pour effectuer cette opération nous nous basons sur le fichier de configuration JCA ou le WSDL du service. Pour les récupérer, nous utilisons une connexion HTTP à l’URL fournie avec authentification basique (les fichiers .jca sont protégés).

ObjectName[] referencesMBeanName = (ObjectName[]) mbeanServerConnection.getAttribute(compositeMBeanName, "References");
for (ObjectName referenceMBeanName: referencesMBeanName)
{
  System.out.println("  Reference : " + extractReference(referenceMBeanName));
  ObjectName[] bindingsMBeanName = (ObjectName[]) mbeanServerConnection.getAttribute(referenceMBeanName, "Bindings");
  for (ObjectName bindingMBeanName : bindingsMBeanName)
  {
    System.out.println("    Binding : " + extractBinding(bindingMBeanName));
    String bindingType = (String) mbeanServerConnection.getAttribute(bindingMBeanName, "BindingType");
    String wsdluri = (String) mbeanServerConnection.getAttribute(bindingMBeanName, "WSDLURI");
    if (bindingType.equals("binding.ws"))
    {
      // méthode récupérant le document à l’URL donnée
      String wsdl = getDocument(wsdluri);
      checkEndpoint(wsdl);
    }
    else if (bindingType.equals("binding.jca")) 
    {
      String config = (String) mbeanServerConnection.getAttribute(bindingMBeanName, "Config");
      String configuri = wsdluri.substring(0,wsdluri.lastIndexOf("/")) + "/" + config;
      // méthode récupérant le document à l’URL donnée (basic authent)
      String configXml = getDocument(configuri);
      checkJca(configXml);
    }
  }
}

 Pour toutes les vérifications, nous utilisons des recherches de pattern, ainsi qu’une comparaison selon des « black » ou « white » list.

Conclusion

Et voilà, nous n’avons plus à présent lors d’un déploiement en intégration ou production qu’à lancer notre batch de vérification. Ceci reste un exemple simplifié.

Les orientations présentées ici ne sont pas les seules, les vérifications peuvent être plus précises, plus lâches. Libre à vous de fixer la limite qui vous parait raisonnable.

Annexe

Voici le code complet permettant de lister (méthodes de contrôles non implémentées)

public class CheckCompositeConfiguration {
    public CheckCompositeConfiguration() {
        super();
    }
    public static void main(String[] arg) throws Exception {
      String host = "soa-vm";
      String port = "8001";
      String user = "weblogic";
      String password = "welcome1";
      // connexion SOA Facade API
      Hashtable props = new Hashtable();
      props.put(Context.PROVIDER_URL, "t3://" + host + ":" + port + "/soa-infra");
      props.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
      props.put(Context.SECURITY_PRINCIPAL, user);
      props.put(Context.SECURITY_CREDENTIALS, password);
      Locator locator = LocatorFactory.createLocator(props);
      // connexion System MBeans
      String jndiroot = "/jndi/";
      MBeanServerConnection mbeanServerConnection = null;
      Map<String,String> jndiJMXProps = new HashMap<String,String>();
      jndiJMXProps.put(Context.SECURITY_PRINCIPAL, user);
      jndiJMXProps.put(Context.SECURITY_CREDENTIALS, password);
      jndiJMXProps.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, "weblogic.management.remote");
      JMXServiceURL serviceURL = new JMXServiceURL("t3", host, Integer.parseInt(port), jndiroot + "weblogic.management.mbeanservers.runtime");
      JMXConnector mbeanConnector = JMXConnectorFactory.newJMXConnector(serviceURL, jndiJMXProps);
      mbeanConnector.connect();
      mbeanServerConnection = mbeanConnector.getMBeanServerConnection();
      // Traitement
      String initialMBeanName = "oracle.soa.config:partition=*,j2eeType=SCAComposite,revision=*,label=*,Application=*,wsconfigtype=WebServicesConfig,name=*";
      Set<ObjectName> compositesMBeansNames = mbeanServerConnection.queryNames(new ObjectName(initialMBeanName), null);
      for (ObjectName compositeMBeanName : compositesMBeansNames)
      {
          String partition = extractCompositePartition(compositeMBeanName);
          String compositeName = extractCompositeName(compositeMBeanName);
          String revision = extractCompositeRevision(compositeMBeanName);
          String compositeDN = partition + "/" + compositeName + "!" + revision;
          System.out.println("Composite : " + compositeDN);
          CompositeFilter filter = new CompositeFilter();
          filter.setCompositeDN(compositeDN);
          List<Composite> composites = locator.getComposites(filter);
          Composite composite = composites.get(0);
          List<ImportInfo> importsinfo = composite.getImports();
          if (importsinfo != null)
          {
              for (ImportInfo importinfo : importsinfo)
              {
                checkImport(importinfo);
              }
          }
          ObjectName[] referencesMBeanName = (ObjectName[]) mbeanServerConnection.getAttribute(compositeMBeanName, "References");
          for (ObjectName referenceMBeanName: referencesMBeanName)
          {
            System.out.println("  Reference : " + extractReference(referenceMBeanName));
            ObjectName[] bindingsMBeanName = (ObjectName[]) mbeanServerConnection.getAttribute(referenceMBeanName, "Bindings");
            for (ObjectName bindingMBeanName : bindingsMBeanName)
            {
                System.out.println("    Binding : " + extractBinding(bindingMBeanName));
                String bindingType = (String) mbeanServerConnection.getAttribute(bindingMBeanName, "BindingType");
                String wsdluri = (String) mbeanServerConnection.getAttribute(bindingMBeanName, "WSDLURI");
                if (bindingType.equals("binding.ws"))
                {
                  String wsdl = getDocument(wsdluri);
                  checkEndpoint(wsdl);
                }
                else if (bindingType.equals("binding.jca")) 
                {
                  String config = (String) mbeanServerConnection.getAttribute(bindingMBeanName, "Config");
                  String configuri = wsdluri.substring(0,wsdluri.lastIndexOf("/")) + "/" + config;
                  String configXml = getDocument(configuri);
                  checkJca(configXml);
                }
            }
      }
      }
    }
    /**
   * Extrait du objectName du composite son nom
   * Exemple objectName :
   *
   * @param objectName
   * @return
   */
  public static String extractCompositeName(ObjectName objectName)
  {
    String search = "name="";
    Pattern pattern = Pattern.compile(search+"([^"]*)");
    Matcher matcher = pattern.matcher(objectName.toString());
    if (matcher.find())
    {
      return matcher.group().substring(search.length());
    }
    return "**noname**";
  }
 /**
  * Extrait du objectName du composite son nom
  * Exemple objectName :
  *
  * @param objectName
  * @return
  */
  public static String extractCompositeRevision(ObjectName objectName)
  {
  String search = "revision=";
  Pattern pattern = Pattern.compile(search+"([^,]*)");
  Matcher matcher = pattern.matcher(objectName.toString());
  if (matcher.find())
  {
    return matcher.group().substring(search.length());
  }
  return "**noname**";
  }
  /**
   * Extrait du objectName du composite sa partition
   * Exemple objectName :
   *
   * @param objectName
   * @return
   */
  public static String extractCompositePartition(ObjectName objectName)
  {
  String search = "partition=";
  Pattern pattern = Pattern.compile(search+"([^,]*)");
  Matcher matcher = pattern.matcher(objectName.toString());
  if (matcher.find())
  {
    return matcher.group().substring(search.length());
  }
  return "**noname**";
  }
  /**
   * Récupère un document à une adresse par connexion HTTP avec Basic Auth
   *
   * @param address
   * @return
   * @throws Exception
   */
  public static String getDocument(String address) throws Exception
  {
    URL url = new URL(address);
    URLConnection connection = url.openConnection();
    String auth = "weblogic:welcome1";
    String authorizationString = "Basic " + new BASE64Encoder().encode(auth.getBytes());
    connection.setRequestProperty("Authorization", authorizationString);
    InputStreamReader isr = new InputStreamReader(connection.getInputStream());
    StringBuffer sb = new StringBuffer();
    int numCharsRead;
    char[] chars = new char[1024];
    numCharsRead = isr.read(chars);
    while (numCharsRead > 0)
    {
      sb.append(chars, 0, numCharsRead);
      numCharsRead = isr.read(chars);
    }
    String result = sb.toString();
    return result;
  }
  public static String extractReference(ObjectName objectName)
    throws Exception
  {
  String search = "oracle.soa.config:name=";
  Pattern pattern = Pattern.compile(search+"([^,]*)");
  Matcher matcher = pattern.matcher(objectName.toString());
  if (matcher.find())
  {
    return matcher.group().substring(search.length());
  }
  return "**noname**";
  }
  public static String extractBinding(ObjectName objectName)
    throws Exception
  {
  String search = "oracle.soa.config:name=";
  Pattern pattern = Pattern.compile(search+"([^,]*)");
  Matcher matcher = pattern.matcher(objectName.toString());
  if (matcher.find())
  {
    return matcher.group().substring(search.length());
  }
  return "**noname**";
  }
  /**
   * Non implémenté
   * Vérifie les valeurs de l'import
   *
   * @param importInfo
   */
  public static void checkImport(ImportInfo importInfo) {
    ;
  }
  /**
   * Non implémenté
   * Vérifie le contenu du WSDL dont le endpoint
   *
   * @param wsdl
   */
  public static void checkEndpoint(String wsdl) {
    ;
  }
  /**
   * Non implémenté
   * Vérifie le contenu de la configuration JCA
   *
   * @param xml
   */
  public static void checkJca(String xml) {
    ;
  }
}