You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jaxme-dev@ws.apache.org by Alexey Loubyansky <al...@jboss.org> on 2003/12/19 14:13:48 UTC

reading from different sources / content navigator

I am glad we agreed on object factory idea :) But it does not solve all 
my unmarshalling problems. (I am sorry, I am talking about my pains here 
but I do need to find a way to recover.)
It occured to be a very long email. I AM SORRY!

Earlier, I wrote about <a 
href="http://marc.theaimsgroup.com/?l=jaxme-dev&m=107105396024144&w=2">* 
population of the same object model from different XML sources</a>
Please, let me know if the problem is not clear.

To solve this problem I created three object factories: 
EjbJarMetaDataFactory for ejb-jar.xml, JBossMetaDataFactory for 
jboss.xml and JBossCMPMetaDataFactory for jbosscmp-jdbc.xml.
But only EjbJarMetaDataFactory created NEW objects. Other two factories 
just populated the object model created by the EjbJarMetaDataFactory.

In code it looked like this:
       ApplicationMetaData metadata = new ApplicationMetaData();

       MetaDataReader reader = new MetaDataReader();

// create object model and read ejb-jar.xml
       InputStream is = getResource("ejb-jar.xml");
       MetaDataFactory factory = new EjbJarMetaDataFactory(metadata);
       reader.parse(is, factory);
       is.close();

// populate object model with jboss.xml info
       is = getResource("jboss.xml");
       factory = new JBossMetaDataFactory(metadata);
       reader.parse(is, factory);
       is.close();

// populate object model with jbosscmp-jdbc.xml info
       is = getResource("jbosscmp-jdbc.xml");
       factory = new JBossCMPMetaDataFactory(metadata);
       reader.parse(is, factory);
       is.close();

So, how it works. Let's consider only one element 'entity' that is 
present in all the XML files and identified by 'ejb-name' simple element.
In EjbJarMetaDataFactory we create a new object:
public Object newChild(ApplicationMetaData appMetaData,
                        String namespaceURI,
                        String localName)
{
    Object child = null;
    if("entity".equals(localName))
    {
       child = new EntityMetaData(appMetaData);
    }
    return child;
}

In other factories, we just return the existing one (for 
JBossMetaDataFactory and JBossCMPMetaDataFactory this is the same):
public Object newChild(ApplicationMetaData appMetaData,
                        String namespaceURI,
                        String localName)
throws MetaDataException
{
    Object child = null;
    if("entity".equals(localName))
    {
       String ejbName = navigator.getChildContent("", "ejb-name");
       child = appMetaData.getBean(ejbName);
    }
    return child;
}

The problem is that I need to return the right entity object which is 
identified by its child ejb-name. newChild is called when the parsing of 
entity element is started. So, here, I need some way to look ahead for 
its ejb-name. Therefore, I introduced the navigator.

Yes, that means that XML source is read and kept in memory.

My in-memory content model is very simple and is represented by Content 
class which implements ContentNavigator which, at the moment, has only 
one method getChildContent(String namespaceURI, String localName).
The design is the subject to change. ContentNavigator could even 
implement XPath if needed.

How is the ContentNavigator is passed to the object factory? Currently, 
I pass it in startDocument() (we agreed to rename it to createRoot()) 
method. Like this:
    public Object startDocument(ContentNavigator navigator)
    {
       this.navigator = navigator;
       return metadata;
    }

Yes, this means, in my implementation, object factories are not 
thread-safe. To make them thread-safe, we could remove the navigator 
from startDocument()/createRoot() and pass it to newChild, setValue and 
other factory methods. Like this:
public Object newChild(ContentNavigator navigator,
                        ApplicationMetaData appMetaData,
                        String namespaceURI,
                        String localName)

!!! THANK YOU FOR READING IT SO FAR !!!

Alexey


---------------------------------------------------------------------
To unsubscribe, e-mail: jaxme-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: jaxme-dev-help@ws.apache.org


Re: reading from different sources / content navigator

Posted by Jochen Wiedmann <jo...@ispsoft.de>.
Hi, Alexey,

sorry for replying late, but as I wrote in a previous post: It takes time
reading your mails and I did not do a lot of mailing in the previous X-mas
days. :-)


> 1. XML schemas.
> For my case there will be 4: ejb-jar, jboss, jbosscmp-jdbc and the 
> merged one with redefinitions.
> I don't modify ejb-jar as it comes from the spec but I do modify others 
> and so, have to keep the merged one in sync with the changes in other 
> schemas. That is a problem.
> XSLT is not an option, as for me, as it is another file to maintain and 
> that could be even worse than maintaining the merged schema manually.
> 
> What are the reasons for the merged schema? I can think of the source 
> generator. And I don't see another way for generating class hierarchy 
> from many schemas w/o merging them.
> Do I need the merged schema on parsing? The documents can be validated 
> against their own schemas. The generated object factory should also work 
> fine w/o it. It seems to me, if I provide the class hierarchy and the 
> factory myself, I could avoid the merged schema.

I am not sure, what your final target is. I would personally think that
you want

   - manually or automatically create a set of Java beans by merging the
     above schemata
   - have one or three parsers, that take as input such a bean and fill
     it with information read from these three files
   - while still maintaining upwards compatibility with existing sources

Please correct me if I am wrong. I am viewing our discussion as the
most simple and extendible way (in terms of reusability) to achieve
the above goals.


> 4. Object tree merger.
> Parsing each document will result in a new object tree. We pick the main 
> one and add the content from the other ones. Visitor pattern fits nicely 
> here. But it is another, potentially, huge class to maintain.
> Also, note, object trees the content was merged from become garbage.
> 
> I don't like this last one. I like the content navigator much more. What 
> is wrong with it? I do not implement the SAX parser, I jusr read the 
> content in memory in the form that can easily be navigated.

The problem is, that the navigator is much more difficult to integrate
in the current framework. The merger fits in very well.

Besides, the class wouldn't be quite complex. In contrary, by adding the
generator for visitors and a default implementation of the merger to
the JaxMe code base, you'd simply have to override certain methods,
most probably in the generator.


Jochen

---------------------------------------------------------------------
To unsubscribe, e-mail: jaxme-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: jaxme-dev-help@ws.apache.org


Re: reading from different sources / content navigator

Posted by Alexey Loubyansky <al...@jboss.org>.
Hello Jochen,

I've got your idea and it is another solution that will work! Now we 
have something to compare.

One note, though, the discussion is heavilly tied to jboss specific 
problems, I would like us to abstract from jboss where possible and keep 
in mind that we are looking at the problem of populating the same object 
model from different sources.

1. XML schemas.
For my case there will be 4: ejb-jar, jboss, jbosscmp-jdbc and the 
merged one with redefinitions.
I don't modify ejb-jar as it comes from the spec but I do modify others 
and so, have to keep the merged one in sync with the changes in other 
schemas. That is a problem.
XSLT is not an option, as for me, as it is another file to maintain and 
that could be even worse than maintaining the merged schema manually.

What are the reasons for the merged schema? I can think of the source 
generator. And I don't see another way for generating class hierarchy 
from many schemas w/o merging them.
Do I need the merged schema on parsing? The documents can be validated 
against their own schemas. The generated object factory should also work 
fine w/o it. It seems to me, if I provide the class hierarchy and the 
factory myself, I could avoid the merged schema.

2. One document class hierarchy.
What we need by definition. We maintain only it.

3. One document object factory for merged schema (i.e. for all the three 
documents).
Good and bad. The good thing is that it is only one. The bad thing is it 
can be messy to handle all the elements in one factory. Also, in case of 
DTDs, when an element is parsed (addChild method in the factory called), 
I don't know from what document it was read, right? Contrary, in case of 
XML schemas, I have a non-empty namespaceURI as a parameter to 
addChild(). Or could we passa systemId in case of DTDs as a namespaceURI?

4. Object tree merger.
Parsing each document will result in a new object tree. We pick the main 
one and add the content from the other ones. Visitor pattern fits nicely 
here. But it is another, potentially, huge class to maintain.
Also, note, object trees the content was merged from become garbage.

I don't like this last one. I like the content navigator much more. What 
is wrong with it? I do not implement the SAX parser, I jusr read the 
content in memory in the form that can easily be navigated.

Thank you,

Alexey

Jochen Wiedmann wrote:

> 
> Hi, Alexey,
> 
> first of all, thanks for the DTD's. Helped me a lot to understand what 
> you mean. After thinking a little bit, I have a completely different 
> suggestion.
> 
> First of all, we already have
> 
>     a) a schema reader, which is able to read a schema (may as well be a
>        DTD, it's not too much of a difference, as far as I cna tell)
>     b) a class generator which creates c) and d)
>     c) Java classes for storing an objects data
>     d) Marshaller and Unmarshaller classes
> 
> Let's first discuss the schema part. My impression is that it is 
> sufficient to to take elements in the latter schemas, find matching 
> elements in the first schema and add elements to them, if not already 
> defined.
> 
> Did you know that XML Schema already contains a mechanism for exactly 
> that? It is called redefinition. I'll demonstrate the use in the case of 
> the root element:
> 
>     ejb-jar.xsd:
> 
>       <xs:complexType name="jbossConfig">
>         <xs:sequence>
>           <xs:element name="description">...</element>
>           <xs:element name="small-icon">...</element>
>         </xs:sequence>
>       </xs:complexType>
> 
>       <xs:element name="ejb-jar" type="jbossConfig"/>
> 
>     jboss.xsd:
> 
>       <xs:redefine schemaLocation="ejb-jar.xsd">
>         <xs:complexType name="jbossConfig">
>           <xs:complexContent>
>             <xs:extension base="jbossConfig">
>               <xs:sequence>
>                 <xs:element name="loader-repository">...</xs:element>
>               </xs:sequence>
>             </xs:extension>
>           </xs:complexContent>
>         </xs:complexType>
>       </xs:redefine>
> 
>       <xs:element name="jboss" type="jbossConfig"/>
> 
> In other words: The elements named "ejb-jar" and "jboss" will have the
> same type, in particular they will have elements named "description",
> "small-icon", ..., "loader-repository", and so on.
> 
> Again using other words: The SAX handler for "jbossConfig" will be able
> to read the files "ejb-jar.xml", "jboss.xml", "jbosscmp-jdbc.xml" and
> create three instances of jbossConfig. We have achieved that by merging
> the schemas. (Note that I omit the problem of mandatory elements.)
> 
> This is something that JaxMe already can do. (Ignoring the fact that
> redefinition is not yet implemented, but it is just a special case of
> extension, so I would offer to do that part.)
> 
> The schema files above can be created by some simple XML transformation,
> using a stylesheet or the like, if the original schema files are available
> in XML Schema. (One can use a DTD converter for that part.)
> 
> 
> Suggest now, that we have three instances of "jbossConfig" in memory. 
> The three instances can be merged into one as follows:
> 
> - Let JaxMe create a default merger. The default merger is an application
>   of the visitor pattern: Suggest that we have a source object S and a
>   target object T. The visitor would walk through all the elements
>   and attributes S. In your special case:
> 
>     - If the attribute is set, invoke T.setAttribute(s.getAttribute())
>     - If the element is simple and set, do the same.
>     - If the element is complex, find a matching element in T (the
>       default merger would create a new one) and invoke the
>       merger on these elements.
> 
> I would offer to create a sample generator for a visitor. You'd be able
> to subclass that.
> 
> 
> Jochen


---------------------------------------------------------------------
To unsubscribe, e-mail: jaxme-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: jaxme-dev-help@ws.apache.org


Re: reading from different sources / content navigator

Posted by Jochen Wiedmann <jo...@ispsoft.de>.
Hi, Alexey,

first of all, thanks for the DTD's. Helped me a lot to understand what you 
mean. After thinking a little bit, I have a completely different suggestion.

First of all, we already have

     a) a schema reader, which is able to read a schema (may as well be a
        DTD, it's not too much of a difference, as far as I cna tell)
     b) a class generator which creates c) and d)
     c) Java classes for storing an objects data
     d) Marshaller and Unmarshaller classes

Let's first discuss the schema part. My impression is that it is sufficient 
to to take elements in the latter schemas, find matching elements in the 
first schema and add elements to them, if not already defined.

Did you know that XML Schema already contains a mechanism for exactly that? 
It is called redefinition. I'll demonstrate the use in the case of the root 
element:

     ejb-jar.xsd:

       <xs:complexType name="jbossConfig">
         <xs:sequence>
           <xs:element name="description">...</element>
           <xs:element name="small-icon">...</element>
         </xs:sequence>
       </xs:complexType>

       <xs:element name="ejb-jar" type="jbossConfig"/>

     jboss.xsd:

       <xs:redefine schemaLocation="ejb-jar.xsd">
         <xs:complexType name="jbossConfig">
           <xs:complexContent>
             <xs:extension base="jbossConfig">
               <xs:sequence>
                 <xs:element name="loader-repository">...</xs:element>
               </xs:sequence>
             </xs:extension>
           </xs:complexContent>
         </xs:complexType>
       </xs:redefine>

       <xs:element name="jboss" type="jbossConfig"/>

In other words: The elements named "ejb-jar" and "jboss" will have the
same type, in particular they will have elements named "description",
"small-icon", ..., "loader-repository", and so on.

Again using other words: The SAX handler for "jbossConfig" will be able
to read the files "ejb-jar.xml", "jboss.xml", "jbosscmp-jdbc.xml" and
create three instances of jbossConfig. We have achieved that by merging
the schemas. (Note that I omit the problem of mandatory elements.)

This is something that JaxMe already can do. (Ignoring the fact that
redefinition is not yet implemented, but it is just a special case of
extension, so I would offer to do that part.)

The schema files above can be created by some simple XML transformation,
using a stylesheet or the like, if the original schema files are available
in XML Schema. (One can use a DTD converter for that part.)


Suggest now, that we have three instances of "jbossConfig" in memory. The 
three instances can be merged into one as follows:

- Let JaxMe create a default merger. The default merger is an application
   of the visitor pattern: Suggest that we have a source object S and a
   target object T. The visitor would walk through all the elements
   and attributes S. In your special case:

     - If the attribute is set, invoke T.setAttribute(s.getAttribute())
     - If the element is simple and set, do the same.
     - If the element is complex, find a matching element in T (the
       default merger would create a new one) and invoke the
       merger on these elements.

I would offer to create a sample generator for a visitor. You'd be able
to subclass that.


Jochen


---------------------------------------------------------------------
To unsubscribe, e-mail: jaxme-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: jaxme-dev-help@ws.apache.org


Re: reading from different sources / content navigator

Posted by Alexey Loubyansky <al...@jboss.org>.
Hello Jochen,

Jochen Wiedmann wrote:

> 
> Hi, Alexey,
> 
> just two short notes before replying in more detail:
> 
> - It would help me to think on your special requirements if
>   I knew the three schemas in question. Could you please be
>   so kind and post them?

Sure, attached.

> - I do not think that you or we are on the right track.
>   Combining object factory and "navigator" in one thing
>   does essentially mean (IMO) that you reproduce the whole SAX
>   parser.

In fact, it's kind of like that. As I wrote, I read the XML source (with 
SAX parser) into an instance of Content class. I just create objects 
that represent SAX events (startElement, endElement, etc) and add them 
to a List. This is the content.
Besides ContentNavigator interface implementation, Content class has the 
following method:
void handleContent(ContentHandler handler);

Now different ContentHandler implementations can be passed in. For 
unmarshalling, the handler will delegate to the object factory. For 
marshalling, I pass the ContentWriter handler that just writes to a Writer.

> Of course this works. However, if you have written
>   the complete SAX parser, what can the generator give you?

Do you mean the java source generator? It should generate the document 
class hierarchy, object factory for unmarshalling and a similar (by 
principle) class for marshalling.

>   (Except perhaps for the marshalling code, but in your
>   special case this might even be ignorable.)

For this special case, you are right. But the problem is not yet clear 
to me.

> 
> IMO your approach only makes sense, if the final result will
> enable you to use considerable parts of the controller (aka
> SAX handler) which is generated by JaxMe.

This is something I am very interested in :) Could you, please, 
elaborate more on this?

Thank you,

Alexey


Re: reading from different sources / content navigator

Posted by Jochen Wiedmann <jo...@ispsoft.de>.
Hi, Alexey,

just two short notes before replying in more detail:

- It would help me to think on your special requirements if
   I knew the three schemas in question. Could you please be
   so kind and post them?


- I do not think that you or we are on the right track.
   Combining object factory and "navigator" in one thing
   does essentially mean (IMO) that you reproduce the whole SAX
   parser. Of course this works. However, if you have written
   the complete SAX parser, what can the generator give you?
   (Except perhaps for the marshalling code, but in your
   special case this might even be ignorable.)

IMO your approach only makes sense, if the final result will
enable you to use considerable parts of the controller (aka
SAX handler) which is generated by JaxMe.


Jochen

---------------------------------------------------------------------
To unsubscribe, e-mail: jaxme-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: jaxme-dev-help@ws.apache.org