Package org.jboss.ha.core.channelfactory

Source Code of org.jboss.ha.core.channelfactory.ProtocolStackUtil

/*
* JBoss, Home of Professional Open Source.
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.jboss.ha.core.channelfactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.jboss.logging.Logger;
import org.jboss.util.StringPropertyReplacer;
import org.jgroups.JChannel;
import org.jgroups.conf.ConfiguratorFactory;
import org.jgroups.util.Util;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* Utilities related to JGroups protocol stack manipulation. This duplicates
* a lot of things in JGroups, but does so in order to eliminate usage of
* classes that JGroups considers to be internal JGroups implementation details
* that are subject to change at any time.
*
* @author Brian Stansberry
*
* @version $Revision: $
*/
public final class ProtocolStackUtil
   private static final Logger log = Logger.getLogger(ProtocolStackUtil.class);
  
   private static final String PROTOCOL_STACKS="protocol_stacks";
   private static final String STACK="stack";
   private static final String NAME="name";
   private static final String DESCR="description";
   private static final String CONFIG="config";

   /**
    * Parses the contents of <code>input</code> into a map of the
    * protocol stack configurations contained in the XML.
    *
    * @param input stream which must contain XML content in the JGroups
    *              <code>stacks.xml</code> format
    *             
    * @return a map of the protocol stack configurations contained in the XML
    *
    * @throws IllegalArgumentException if <code>input</code> is <code>null</code>
    * @throws Exception
    */
   public static Map<String, ProtocolStackConfigInfo> parse(InputStream input) throws Exception
   {
      if (input == null)
      {
         throw new IllegalArgumentException("null input");
      }
     
      DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
      factory.setValidating(false); //for now
      DocumentBuilder builder=factory.newDocumentBuilder();
      Document document=builder.parse(input);

      // The root element of the document should be the "protocol_stacks" element,
      // but the parser(Element) method checks this so a check is not
      // needed here.
      Element configElement = document.getDocumentElement();
      return parse(configElement);
   }
  
   /**
    * Parses the contents of <code>root</code> into a map of the
    * protocol stack configurations contained in the XML.
    *
    * @param root document root node for XML content in the JGroups
    *              <code>stacks.xml</code> format
    *             
    * @return a map of the protocol stack configurations contained in the XML
    *
    * @throws IllegalArgumentException if <code>input</code> is <code>null</code>
    * @throws Exception
    */
   public static Map<String, ProtocolStackConfigInfo> parse(Element root) throws Exception
   {
      if (root == null)
      {
         throw new IllegalArgumentException("null root");
      }
     
      String root_name = root.getNodeName();
      if (!PROTOCOL_STACKS.equals(root_name.trim().toLowerCase()))
      {
         throw new IOException("Invalid XML configuration: configuration does not start with a '" +
                        PROTOCOL_STACKS + "' element");
      }

      Map<String, ProtocolStackConfigInfo> result = new HashMap<String, ProtocolStackConfigInfo>();

      NodeList tmp_stacks = root.getChildNodes();
      for (int i = 0; i < tmp_stacks.getLength(); i++)
      {
         Node node = tmp_stacks.item(i);
         if (node.getNodeType() != Node.ELEMENT_NODE)
            continue;

         Element stack = (Element) node;
         String tmp = stack.getNodeName();
         if (!STACK.equals(tmp.trim().toLowerCase()))
         {
            throw new IOException("Invalid configuration: didn't find a \"" + STACK + "\" element under \""
                  + PROTOCOL_STACKS + "\"");
         }

         NamedNodeMap attrs = stack.getAttributes();
         Node name = attrs.getNamedItem(NAME);
         String st_name = name.getNodeValue();
         Node descr=attrs.getNamedItem(DESCR);
         String stack_descr=descr.getNodeValue();
         if (log.isTraceEnabled())
         {
            log.trace("Parsing \"" + st_name + "\" (" + stack_descr + ")");
         }
         NodeList configs = stack.getChildNodes();
         for (int j = 0; j < configs.getLength(); j++)
         {
            Node tmp_config = configs.item(j);
            if (tmp_config.getNodeType() != Node.ELEMENT_NODE)
               continue;
            Element cfg = (Element) tmp_config;
            tmp = cfg.getNodeName();
            if (!CONFIG.equals(tmp))
            {
               throw new IOException("Invalid configuration: didn't find a \"" +
                     CONFIG + "\" element under \"" + STACK + "\"");
            }

            ProtocolData[] protocolData = parseConfig(cfg);
            // replace vars with system props
            substituteVariables(protocolData);

            result.put(st_name, new ProtocolStackConfigInfo(st_name, stack_descr, protocolData));
         }
      }

      return result;
   }  
  
   public static ProtocolData[] parseSingleConfig(InputStream input) throws Exception
   {
      if (input == null)
      {
         throw new IllegalArgumentException("null input");
      }
     
      DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
      factory.setValidating(false); //for now
      DocumentBuilder builder=factory.newDocumentBuilder();
      Document document=builder.parse(input);

      // The root element of the document should be the "config" element,
      // but the parser(Element) method checks this so a check is not
      // needed here.
      Element configElement = document.getDocumentElement();
      return parseConfig(configElement);
   }
  
   public static ProtocolData[] parseConfig(Element root_element) throws java.io.IOException
   {

      /** LinkedList<ProtocolData> */
      List<ProtocolData> prot_data = new LinkedList<ProtocolData>();

      /**
       * CAUTION: crappy code ahead ! I (bela) am not an XML expert, so the
       * code below is pretty amateurish... But it seems to work, and it is
       * executed only on startup, so no perf loss on the critical path. If
       * somebody wants to improve this, please be my guest.
       */
      try
      {
         String root_name = root_element.getNodeName();
         if (!"config".equals(root_name.trim().toLowerCase()))
         {
            log.fatal("XML protocol stack configuration does not start with a '<config>' element; "
                  + "maybe the XML configuration needs to be converted to the new format ?\n"
                  + "use 'java org.jgroups.conf.XmlConfigurator <old XML file> -new_format' to do so");
            throw new IOException("invalid XML configuration");
         }

         NodeList prots = root_element.getChildNodes();
         for (int i = 0; i < prots.getLength(); i++)
         {
            Node node = prots.item(i);
            if (node.getNodeType() != Node.ELEMENT_NODE)
               continue;

            Element tag = (Element) node;
            String protocol = tag.getTagName();
            // System.out.println("protocol: " + protocol);
            LinkedList<ProtocolParameter> params = new LinkedList<ProtocolParameter>();

            NamedNodeMap attrs = tag.getAttributes();
            int attrLength = attrs.getLength();
            for (int a = 0; a < attrLength; a++)
            {
               Attr attr = (Attr) attrs.item(a);
               String name = attr.getName();
               String value = attr.getValue();
               // System.out.println("    name=" + name + ", value=" + value);
               params.add(new ProtocolParameter(name, value));
            }
            ProtocolData data = new ProtocolData(protocol, protocol, params);
            prot_data.add(data);
         }

         return prot_data.toArray(new ProtocolData[prot_data.size()]);
      }
      catch (Exception x)
      {
         if (x instanceof java.io.IOException)
            throw (java.io.IOException) x;
         else
         {
            throw new IOException(x);
         }
      }
   }

   public static void substituteVariables(ProtocolData[] protocols)
   {
      for (ProtocolData data : protocols)
      {
         if (data != null)
         {
            for (ProtocolParameter param : data.getParametersAsArray())
            {
               String val = param.getValue();
               // We use jboss-common-core here so we get the richer feature set
               // of that versus JGroups system property replacement
               String replacement = StringPropertyReplacer.replaceProperties(val);
               if (!replacement.equals(val))
               {
                  param.setValue(replacement);
               }
            }
         }
      }
   }

   public static String getProtocolStackString(ProtocolData[] protocolData)
   {
      StringBuilder buf = new StringBuilder();
      for (int i = 0; i < protocolData.length; i++)
      {
         ProtocolData pd = protocolData[i];
         buf.append(pd.getProtocolString());
         if (i < protocolData.length - 1)
         {
            buf.append(':');
         }
      }
      return buf.toString();
   }

   private static List<String> parseProtocols(String config_str) throws IOException
   {
      List<String> retval = new LinkedList<String>();
      PushbackReader reader = new PushbackReader(new StringReader(config_str));
      int ch;
      StringBuilder sb;
      boolean running = true;

      while (running)
      {
         String protocol_name = readWord(reader);
         sb = new StringBuilder();
         sb.append(protocol_name);

         ch = read(reader);
         if (ch == -1)
         {
            retval.add(sb.toString());
            break;
         }

         if (ch == ':')
         { // no attrs defined
            retval.add(sb.toString());
            continue;
         }

         if (ch == '(')
         { // more attrs defined
            reader.unread(ch);
            String attrs = readUntil(reader, ')');
            sb.append(attrs);
            retval.add(sb.toString());
         }
         else
         {
            retval.add(sb.toString());
         }

         while (true)
         {
            ch = read(reader);
            if (ch == ':')
            {
               break;
            }
            if (ch == -1)
            {
               running = false;
               break;
            }
         }
      }
      reader.close();

      return retval;
   }

   private static int read(Reader reader) throws IOException
   {
      int ch = -1;
      while ((ch = reader.read()) != -1)
      {
         if (!Character.isWhitespace(ch))
            return ch;
      }
      return ch;
   }

   private static String readUntil(Reader reader, char c) throws IOException
   {
      StringBuilder sb = new StringBuilder();
      int ch;
      while ((ch = read(reader)) != -1)
      {
         sb.append((char) ch);
         if (ch == c)
            break;
      }
      return sb.toString();
   }

   private static String readWord(PushbackReader reader) throws IOException
   {
      StringBuilder sb = new StringBuilder();
      int ch;

      while ((ch = read(reader)) != -1)
      {
         if (Character.isLetterOrDigit(ch) || ch == '_' || ch == '.' || ch == '$')
         {
            sb.append((char) ch);
         }
         else
         {
            reader.unread(ch);
            break;
         }
      }

      return sb.toString();
   }

   private static ProtocolData parseProtocol(String protocolConfig) throws Exception
   {
      String protocol_name = null;
      String properties_str = null;
      int index = protocolConfig.indexOf('('); // e.g. "UDP(in_port=3333)"
      int end_index = protocolConfig.lastIndexOf(')');

      if (index == -1)
      {
         protocol_name = protocolConfig;
         properties_str = "";
      }
      else
      {
         if (end_index == -1)
         {
            throw new Exception("Configurator.ProtocolConfiguration(): closing ')' " + "not found in " + protocolConfig
                  + ": properties cannot be set !");
         }
         else
         {
            properties_str = protocolConfig.substring(index + 1, end_index);
            protocol_name = protocolConfig.substring(0, index);
         }
      }
      List<ProtocolParameter> params = parsePropertiesString(protocol_name, properties_str);

      return new ProtocolData(protocol_name, protocol_name, params);
   }

   private static List<ProtocolParameter> parsePropertiesString(String protocol_name, String properties_str)
         throws Exception
   {
      List<ProtocolParameter> params = new ArrayList<ProtocolParameter>();
      int index = 0;

      /* "in_port=5555;out_port=6666" */
      if (properties_str.length() > 0)
      {
         String[] components = properties_str.split(";");
         for (String property : components)
         {
            String name, value;
            index = property.indexOf('=');
            if (index == -1)
            {
               throw new Exception("'=' not found in " + property + " of " + protocol_name);
            }
            name = property.substring(0, index);
            value = property.substring(index + 1, property.length());
            params.add(new ProtocolParameter(name, value));
         }
      }
      return params;
   }   
  
  
   public static ProtocolData[] getProtocolData(Object properties) throws Exception {
      InputStream input=null;

      // added by bela: for null String props we use the default properties
      if(properties == null)
          properties=JChannel.DEFAULT_PROTOCOL_STACK;

      if(properties instanceof URL) {
          try {
              input=((URL)properties).openStream();
          }
          catch(Throwable t) {
          }
      }

      // if it is a string, then it could be a plain string or a url
      if(input == null && properties instanceof String) {
          try {
              input=new URL((String)properties).openStream();
          }
          catch(Exception ignore) {
              // if we get here this means we don't have a URL
          }

          // another try - maybe it is a resource, e.g. udp.xml
          if(input == null && ((String)properties).endsWith("xml")) {
              try {
                  input=Util.getResourceAsStream((String)properties, ConfiguratorFactory.class);
              }
              catch(Throwable ignore) {
              }
          }

          // try a regular file name
          //
          // This code was moved from the parent block (below) because of the
          // possibility of causing a ClassCastException.

          if(input == null) {
              try {
                  input=new FileInputStream((String)properties);
              }
              catch(Throwable t) {
              }
          }
      }

      // try a regular file
      if(input == null && properties instanceof File) {
          try {
              input=new FileInputStream((File)properties);
          }
          catch(Throwable t) {
          }
      }

      if(input != null) {
          return parseSingleConfig(input);
      }

      if(properties instanceof Element) {
          return parseConfig((Element)properties);
      }

      return parseProtocolStackString((String)properties);
  }

   private static ProtocolData[] parseProtocolStackString(String configuration) throws Exception
   {
      List<ProtocolData> retval = new ArrayList<ProtocolData>();
      List<String> protocol_string = parseProtocols(configuration);

      if (protocol_string == null)
         return null;

      for (String component_string : protocol_string)
      {
         retval.add(parseProtocol(component_string));
      }
      return retval.toArray(new ProtocolData[retval.size()]);
   }
  
   /**
    * Prevent instantiation.
    */
   private ProtocolStackUtil()
   {
   }

}
TOP

Related Classes of org.jboss.ha.core.channelfactory.ProtocolStackUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.