简体   繁体   中英

processing xml configuration documents in spring beans

In our application we've some static xml configuration for accessing routes. An example of an xml file looks like:

 <configuration>
   <level value="1" progress="1">
     <route destination="a">ba</route>
   </level>
   <level value="1" progress="2">
     <route destination="a">caba</route>
     <route destination="b">cabb</route> 
   </level>
   .. etc ..
 </configuration>

On multiple occasion we need to retrieve the value(s) of the route, given arguments value , progress , and destination (all optional, no arguments at all should return all routes).

I know how to achieve this with XPath, but I would like to use it in a spring bean, which can be wired into other spring beans, services.

I'm thinking something like

 @Service
 Class RouteConfiguration implements IRouteConfiguration {
      Document xmlRoutes = null;
      XPath xPath =  XPathFactory.newInstance().newXPath();
      // Constructor
      public RouteConfiguration() {
          try {
              DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
              DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
              xmlRoutes = docBuilder.parse (this.getClass().getResourceAsStream(url));
          } catch AndAll...



      // method
      public List<String> getRoutes(Integer level, Integer progress, String destination) {
            String expression = String.format("/configuration/level[@value='%d' and @progress='%d']/route[@destination='%s']", level, progress, destination);
            NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(xmlRoutes, XPathConstants.NODESET);
            // Of course some more plumbing needed to cater for optional arguments and to get result List.
      } 

I'm wondering is this the right approach? I know that Spring has XML support, but as far as I can see this applies for xml (webservice) messages. Also I'm worried about concurrency and possible performance issues? Would there be better solutions to tackle this (I can create an Object Graph out of the xsd and use jxpath, or plain java code to get through the final result)?

You should take a look at the Marshalling XML using O/X Mappers from the Spring reference. Perhaps you will find the answer.

Another Option would be to use Jaxb with spring using java Pojo.

"I'm wondering is this the right approach?"

To fully answer your question, I believe more information would be needed. How many config files are there? How big are they? Are they required to be stored in XML? To answer whether this is the right approach is hard to say without such information.

If you simply care about the Spring approach, I'd recommend using an InitializingBean for loading your XML configs. Here is a recent article (not mine, but seems sane) http://www.journaldev.com/2637/spring-bean-life-cycle-methods-initializingbean-disposablebean-postconstruct-predestroy-aware-interfaces

As for concurrency and performance, I'd recommend (depending on how many there are) that you load them all into memory using an InitializingBean on app startup and then your performance is fine, and concurrency is the same as any other Spring Bean.

Jaxb is a nice and powerfull tool mainly if you create an xsd for your xml file. As it is currently declared, you could have something like :

<schema version="1.0"
        xmlns="http://www.w3.org/2001/XMLSchema"
        xmlns:conf="http://my/schemas/Config"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        targetNamespace="http://my/schemas/Config"
        elementFormDefault="qualified"
        xsi:schemaLocation="http://www.w3.org/2001/XMLSchema http://www.w3.org/2001/XMLSchema.xsd">
    <complexType name="route">
        <simpleContent>
            <extension base="string">
                <attribute name="destination" type="string"/>
            </extension>
        </simpleContent>
    </complexType>
    <complexType name="level">
        <sequence>
            <element name="route" type="conf:route" minOccurs="1" maxOccurs="unbounded"/>
        </sequence>
        <attribute name="value" type="integer"/>
        <attribute name="progress" type="integer"/>
    </complexType>
    <complexType name="configuration" mixed="true">
        <sequence>
            <element name="level" type="conf:level" minOccurs="1" maxOccurs="unbounded"/>
        </sequence>
    </complexType>
    <element name="configuration" type="conf:configuration"/>
</schema>

and you xml file could then be :

<configuration
        xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
        xmlns='http://my/schemas/Config'
        xsi:schemaLocation='http://my/schemas/Config conf.xsd'>>
   <level value="1" progress="1">
     <route destination="a">ba</route>
   </level>
   <level value="1" progress="2">
     <route destination="a">caba</route>
     <route destination="b">cabb</route> 
   </level>
   ...
</configuration>

Jaxb will then generate your pojos and support classes by

xjc -d directory -p package conf.xsd

And you load the xml via something like :

    Configuration configuration;
    try {
        JAXBContext ctx = JAXBContext.newInstance(Configuration.class.getPackage().getName());
        JAXBElement<Configuration> elt = (JAXBElement<Configuration>) ctx
                .createUnmarshaller().unmarshal(rsrc.getInputStream());
        configuration = elt.getValue();
    } catch (JAXBException ex) {
        throw ex;
    }

One option would be to use an in-memory database (eg H2) and read, flatten and insert the XML data into a table with columns (value, progress, destination, route), and then just run a SQL query against that:

select route from table where value = 1 and progress = 2;

Omitted arguments would not be included in the query.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM