Announcement Announcement Module
Collapse
No announcement yet.
XML config - Namespace with 2.0M3 Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • XML config - Namespace with 2.0M3

    Hey,

    I'm trying to define a new namespace with Spring2.0m3.
    Everything is ok with a basic namespace and a tag that accepts only a set of attributes.
    Now, I would like to create a tag that can accept a "content", like we can do it with the basic <bean> tag and its property set.

    What I would like to parse is :

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:mcip="http://www.mycompany.com/schema/mcip"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.mycompany.com/schema/mcip  mcip.xsd">
    
        <mcip:bean1 id="MyBean1" nom="Gollot" prenom="Erik">
        	<mcip:beans2list name="mesBeans2">
    	    	<beans:ref bean="MyBean2"/>
     		<beans:ref bean="MyBean2"/>
    	    </mcip:beans2list>
        </mcip:bean1>
        <mcip:bean2 id="MyBean2" nom="Oury" prenom="Catherine"/>
     
     </beans>
    I don't know how to create my .xsd for the beans2list tag and how to handle this tag in my namespacehandler.
    I tried to reuse the "beans:ref" tag defined into spring-beans.xsd but I don't know if it's a good idea.

    In fact, I wonder if it is possible to easily reuse an existing tag + an existing namespace handler to create a new tag that accept basic properties as attributes and a content just like we do it in the traditional DTD + define some "alias" for some tags.

    I don't know if it's clear enough ?!

    Thanks

  • #2
    I tried using a registerBeanDefinitionDecorator for a nested element as I had a need to set a property named "id" - which the beanDefintionParser treats specially. I named a nested element as say : '<bean-class>Id'. Didn't work. Javadoc says BeanDefinitionDecorator is for nested custom tags - ( has to be inside only a bean tag??) but what if the top level bean tag itself is now a custom tag that has a namespace handler??

    Comment


    • #3
      I found a solution to my "problem".
      In fact, the applicationContext.xml that I want to be able to parse is :

      Code:
      <?xml version="1.0" encoding="UTF-8"?>
      
      <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:m="http://www.bnpparibas.com/schema/mcip"
          xmlns:beans="http://www.springframework.org/schema/beans"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
          http://www.bnpparibas.com/schema/mcip  mcip.xsd">
      
         	<m:activite id="activite1" name="mon activite1">
      		<m:responsibles>
      			<m:responsible ref="activite2"></m:responsible>
      		</m:responsibles>
      	</m:activite>		
       
       	<m:activite id="activite2" name="mon activite 2">
       		<m:description><![CDATA[<ul> ddd </ul>]]></m:description>
       	</m:activite>
       </beans>
      The corresponding schema is :

      Code:
      <?xml version="1.0" encoding="UTF-8"?>
      <xsd:schema xmlns="http://www.bnpparibas.com/schema/mcip" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.bnpparibas.com/schema/mcip" elementFormDefault="qualified" attributeFormDefault="unqualified">
      	
      	<xsd:complexType name="responsibleType">
      		<xsd:attribute name="ref" type="xsd:string" use="required"/>
      	</xsd:complexType>
      	<xsd:complexType name="responsibleListType">
      		<xsd:sequence>
      			<xsd:element name="responsible" type="responsibleType" maxOccurs="unbounded"/>
      		</xsd:sequence>
      	</xsd:complexType>
      	<xsd:complexType name="descriptionType">
      		<xsd:simpleContent>
      			<xsd:extension base="xsd:string"/>
      		</xsd:simpleContent>
      	</xsd:complexType>
      	<xsd:element name="activite">
      		<xsd:complexType>
      			<xsd:sequence>
      				<xsd:element name="responsibles" type="responsibleListType" minOccurs="0"/>
      				<xsd:element name="description" type="descriptionType" minOccurs="0"/>
      			</xsd:sequence>
      			<xsd:attribute name="name" type="xsd:string" use="required"/>
      			<xsd:attribute name="id" type="xsd:string" use="required"/>
      			<xsd:attribute name="description" type="xsd:string" use="optional"/>
      		</xsd:complexType>
      	</xsd:element>
      	<xsd:element name="activities">
      		<xsd:complexType>
      			<xsd:sequence>
      				<xsd:element ref="activite" maxOccurs="unbounded"/>
      			</xsd:sequence>
      		</xsd:complexType>
      	</xsd:element>
      </xsd:schema>
      And the tag handler is :

      Code:
      public class MyBeansNamespaceHandler extends NamespaceHandlerSupport {
      
      	  public MyBeansNamespaceHandler() {
      	    registerBeanDefinitionParser("activite", new MyActiviteBeanDefinitionParser());	    
      	    
      	  }
      
      	 	  
      	  private static class MyActiviteBeanDefinitionParser extends
      	      AbstractSimpleBeanDefinitionParser {
      
      	    protected Class getBeanClass(Element element) {
      	      return Activite.class;
      	    }
      
      	    protected void postProcess(BeanDefinitionBuilder definitionBuilder,
      	        Element element) {
      	    	// Responsibles
      	    	List responsibles = DomUtils.getChildElementsByTagName(element,"m:responsibles");
      	    	if (responsibles !=null && (responsibles.size()>0)) {
      	    		List responsiblesElement = DomUtils.getChildElementsByTagName((Element)responsibles.get(0),"m:responsible");
      	    		Iterator i = responsiblesElement.iterator();
      	    		Element responsibleElement;
      	    		String ref;
      	    		RuntimeBeanReference beanRef;
      	    		ManagedList beanRefs = new ManagedList();
      	    		while (i.hasNext()) {
      	    			responsibleElement = (Element)i.next();
      	    			ref = responsibleElement.getAttribute("ref");
      	    			System.out.println("adding bean reference : "+ ref);
      	    			beanRef = new RuntimeBeanReference(ref);
      	    			beanRefs.add(beanRef);
      	    			
      	    		}	    		
      	    		definitionBuilder.getBeanDefinition().getPropertyValues().addPropertyValue("mesBeans2",beanRefs);	    			    
      	    	}
      	    	List descriptionList = DomUtils.getChildElementsByTagName(element,"m:description");
      	    	if (descriptionList.size()==1) {
      	    		Element descElement = (Element)descriptionList.get(0);
      	    		System.out.println("descrition cdata : " + DomUtils.getTextValue(descElement));
      	    	}
      	    	
      	    	
      	    	//definitionBuilder.getBeanDefinition().getPropertyValues().addPropertyValue("mesBeans2",defaultRefs);
      	    }
      	    
      	  }
      	  
      	
      	  
      	}
      Of course, the code of the handler is an experimental one but it gives the philosophy of the parsing.
      I will wait for the official documentation of Spring 2.0 to be sure that I've given a good answer to my problem.

      Comment


      • #4
        addPropertyReference() in BeanDefinitionBuilder

        addPropertyReference() in BeanDefinitionBuilder might cut few lines of your code.

        In general after going through some source surrounding the NameSpaceHandler classes, I think what would be useful is an enhancement to NameSpaceHandler & NameSpaceHandlerSupport that allows you to register NestedPropertyDefinitionParser for a nested property tag. And a new AbstractComplexBeanDefinitionParser implementation that reads from registered property parsers. This parser will handle either Nested inline bean definitions - where my property bean is not Id'ed and managed as well as "Property Refs".

        This is just proceeding in the same pattern as the top level bean parsing implementation which currently supports parsing String attributes of a tag and setting simple property values.

        Else Instead of inheritance hierarchy exposing the registration of property parsers, we could just add it to the extension of NameSpaceHandlerSupport and delegate from postProcess() to these registered property parsers.

        Comment


        • #5
          Thanks for addPropertyReference.
          And I agree with NestedPropertyDefinitionParser.

          Thank !

          Do you know a better way to add a property which is a list of bean references than using a ManagedList and RuntimeBeanReference ?

          Here is a class utility I've created to help me to parse my tags (sorry for the comments that are in French !).
          Code:
          package com.bnpp.mcip.config;
          
          import java.lang.reflect.Method;
          import java.util.HashMap;
          import java.util.List;
          
          import org.springframework.beans.factory.config.RuntimeBeanReference;
          import org.springframework.beans.factory.support.AbstractBeanDefinition;
          import org.springframework.beans.factory.support.ManagedList;
          import org.springframework.beans.factory.support.RootBeanDefinition;
          import org.springframework.util.xml.DomUtils;
          import org.w3c.dom.Element;
          import org.w3c.dom.NamedNodeMap;
          import org.w3c.dom.Node;
          
          public abstract class BeanDefinitionParserUtil {
          	/**
          	 * Cette méthode permet de positionner une propriété définie sous la forme
          	 * d'un élément dont la valeur est de type texte, si elle existe. <br>
          	 * Exemple :<br>
          	 * 
          	 * <code>
          	 * &lt;...&gt;<br/>
          	 * 	&nbsp;&lt;m:desciption&gt;le texte de la description&lt;/m:description&gt;<br/>
          	 * &lt;/...&gt;<br/>
          	 * </code>
          	 * 
          	 * @param element
          	 *            l'élément de départ contenant le tagName à chercher
          	 * @param property
          	 *            le nom de la propriété de l'objet à positionner avec la valeur
          	 *            du tag
          	 * @param tagName
          	 *            le nom du tag à rechercher dans l'élément
          	 * @param definition
          	 *            la définition courante dans laquelle ajouter la définition de
          	 *            la propriété
          	 */
          	public static void declare(Element element, String property,
          			String tagName, AbstractBeanDefinition definition) {
          		List l = DomUtils.getChildElementsByTagName(element, tagName);
          		if (l.size() == 1) {
          			Element e = (Element) l.get(0);
          			definition.getPropertyValues().addPropertyValue(property,
          					DomUtils.getTextValue(e));
          		}
          
          	}
          
          	/**
          	 * Cette méthode permet de positionner une propriété définie sous la forme
          	 * d'une <b>liste d'éléments</b>. Chaque élément de la liste est supposé
          	 * avoir défini un attribut de nom <b>ref</b> correspondant à l'id d'un
          	 * autre objet déclaré dans le fichier de configuration <br>
          	 * Exemple :<br>
          	 * 
          	 * <code>
          	 * &lt;...&gt;<br/>
          	 * 	&nbsp;&lt;m:responsables&gt;<br/>
          	 *   	&nbsp;&nbsp;&lt;m:responsable ref="id1"/&gt;<br/>
          	 *  	&nbsp;&nbsp;&lt;m:responsable ref="id2"/&gt;<br/>
          	 *  &nbsp;&lt;/m:responsables&gt;<br/>
          	 * &lt;/...&gt;
          	 * </code>
          	 * 
          	 * @param element
          	 *            l'élément de départ contenant le tagListName à chercher
          	 * @param property
          	 *            le nom de la propriété de l'objet à positionner avec la valeur
          	 *            de tagListName
          	 * @param tagListName
          	 *            le nom du tag représentant l'élément de type liste à
          	 *            rechercher
          	 * @param tagName
          	 *            le nom du tag des éléments de type élement de la liste à
          	 *            rechercher
          	 * @param definition
          	 *            la définition courante dans laquelle ajouter la définition de
          	 *            de la propriété
          	 * 
          	 */
          	public static void declareListRef(Element element, String property,
          			String tagListName, String tagName,
          			AbstractBeanDefinition definition) {
          		List l = DomUtils.getChildElementsByTagName(element, tagListName);
          		if (l.size() == 1) {
          			Element e = (Element) l.get(0);
          			List elts = DomUtils.getChildElementsByTagName(e, tagName);
          			ManagedList refs = new ManagedList();
          			RuntimeBeanReference ref;
          			for (int i = 0; i < elts.size(); i++) {
          				e = (Element) elts.get(i);
          				ref = new RuntimeBeanReference(e.getAttribute("ref"));
          				refs.add(ref);
          			}
          			definition.getPropertyValues().addPropertyValue(property, refs);
          		}
          
          	}
          
          	/**
          	 * Cette méthode permet de positionner une propriété de type référence vers
          	 * un autre objet et défini comme sous-élément d'un bean <br>
          	 * Exemple :<br>
          	 * 
          	 * <code>
          	 * &lt;...&gt;<br/>
          	 * 	&nbsp;&lt;m:parent ref="id1"/&gt;<br/>
          	 * &lt;/...&gt;<br/>
          	 * </code>
          	 * 
          	 * @param element
          	 *            l'élément de départ contenant le tagName à chercher
          	 * @param property
          	 *            le nom de la propriété de l'objet à positionner avec la valeur
          	 *            de Name
          	 * @param tagName
          	 *            le tag à rechercher sous l'élément. Ce tag doit avoir un
          	 *            attribut <b>ref</b>
          	 * @param definition
          	 *            la définition courante dans laquelle ajouter la définition de
          	 *            de la propriété
          	 */
          	public static void declareRef(Element element, String property,
          			String tagName, AbstractBeanDefinition definition) {
          		List l = DomUtils.getChildElementsByTagName(element, tagName);
          		if (l.size() == 1) {
          			Element e = (Element) l.get(0);
          			RuntimeBeanReference ref;
          			ref = new RuntimeBeanReference(e.getAttribute("ref"));			
          			definition.getPropertyValues().addPropertyValue(property, ref);
          		}
          
          	}
          
          	public static void declareObjects(Element element, String property,
          			String tagName, Class clazz, HashMap mapping,
          			AbstractBeanDefinition definition) {
          		List l = DomUtils.getChildElementsByTagName(element, tagName);
          
          		RootBeanDefinition childDefinition;
          		ManagedList childs = new ManagedList();
          		for (int i = 0; i < l.size(); i++) {
          			Element e = (Element) l.get(i);
          			childDefinition = new RootBeanDefinition(clazz);
          			parseChild(e, mapping, childDefinition);
          			childs.add(childDefinition);
          		}
          		definition.getPropertyValues().addPropertyValue(property, childs);
          	}
          	
          	public static void declareObjectsAsList(Element element, String property,
          			String tagListName, String tagName, Class clazz, HashMap mapping,
          			AbstractBeanDefinition definition) {
          		List l = DomUtils.getChildElementsByTagName(element, tagListName);
          
          		if (l.size()==1) {
          			Element e = (Element)l.get(0);
          			declareObjects(e,property,tagName,clazz,mapping,definition);
          		}
          	}
          
          	public static void declareObject(Element element, String property,
          			String tagName, Class clazz, HashMap mapping,
          			AbstractBeanDefinition definition) {
          		List l = DomUtils.getChildElementsByTagName(element, tagName);
          
          		if (l.size() == 1) {
          			Element e = (Element) l.get(0);
          			RootBeanDefinition childDefinition = new RootBeanDefinition(clazz);
          			parseChild(e, mapping, childDefinition);
          			definition.getPropertyValues().addPropertyValue(property,
          					childDefinition);
          		}
          	}
          
          	private static void parseChild(Element e, HashMap mapping,
          			AbstractBeanDefinition definition) {
          		NamedNodeMap attributes = e.getAttributes();
          		Node n;
          		String attrName, attrValue;
          		Class clazz = definition.getBeanClass();
          		for (int i = 0; i < attributes.getLength(); i++) {
          			n = attributes.item(i);
          			attrName = n.getNodeName();
          			attrValue = n.getNodeValue();
          			// Si on a une table de mapping pour les attributs, on change le nom
          			// de l'attribut avec sa correspondance dans le mapping si cette
          			// correspondance existe
          			if (mapping != null && mapping.get(attrName) != null)
          				attrName = (String) mapping.get(attrName);
          			try {				
          				Class getterType = getGetter(clazz,attrName);
          				if (getterType!=null && getterType.isAssignableFrom(String.class)) {					
          					definition.getPropertyValues().addPropertyValue(attrName,
          							attrValue);
          				} else {
          					// On part ici du principe que si la propriété de l'objet
          					// n'est pas de type String, il faut définir une référence
          					// vers un bean déclaré dans le fichier de configuration
          					definition.getPropertyValues().addPropertyValue(attrName,
          							new RuntimeBeanReference(attrValue));
          				}
          			} catch (SecurityException ex) {
          				ex.printStackTrace();
          			}
          		}
          	}
          
          	private static Class getGetter(Class clazz, String attrName) {
          		Method methods[] = clazz.getMethods();
          		String getterName="get"+attrName.substring(0,1).toUpperCase()+attrName.substring(1);
          		for (int i=0;i<methods.length;i++) {
          			if (getterName.equals(methods[i].getName()))
          				return methods[i].getReturnType();
          		}
          		return null;
          	}
          	
          }

          Comment

          Working...
          X