Hi folks,
It's not quite done yet, but I have a few more questions to ask so I
figured I should show you I'm actually working on delivering that howto
that I promised :-).
This howto takes a building approach, doing the simpler version and then
adding the new fields bit by bit.
This is the first draft, which I obviously need to review and edit, and
then format using the HowTo xml tags.
I also need to finish going through the examples and converting them all
to use my new naming scheme. To make it easier for the reader to
understand the mapping files, I'm naming every tag and java class instance
variable specially, i.e. <CustomerTag> gets mapped to the instanceCustomer
variable of the Order class.
How To Map (slightly) Complex Schemas
Introduction to writing Castor mappings to map slightly more
complex pre-defined
schemas to slightly more complex pre-existing Java classes.
Audience
Anyone who has to deal with existing XML document formats and
existing Java
classes, which do not necessarily correspond cleanly.
This document provides a slightly more complex example XML schema and
appropriate
mapping.
Prerequisites
This example was developed under Castor 0.9.9.1 but does not
appear to depend on any version-specific features.
However, for the purposes of this how-to, I'm not going to leave
anything up to chance. Here are the specific jars that I have
downloaded and in my classpath for both compiling and running the test
classes. Note that some of these jar files downloaded without a
version number in the file name, for example xerces-xmp-apis.jar, but
I renamed them to keep track of their version number:
castor-0.9.9.1-xml.jar
commons-logging-api_1_0_4.jar
commons-logging_1_0_4.jar
xerces-xml-apis_2_8_0.jar
xercesImpl_2_8_0.jar
I compiled and ran these tests with j2sdk1.4.2_06.
I also used a castor.properties file containing the sole
line:
-----
org.exolab.castor.debug=true
-----
This turns on the verbose logging messages in Castor, which
aren't terribly helpful, but at least you'll know for certain which
data tag your mapping is choking on.
Arbitrary XML and Arbitrary Java
<P>We chose to use Castor for our project because we need to read and
write XML; specifically we need to write XML messages based on the
values in our java objects, and we need to read XML messages and
instantiate appropriate java objects. The gotcha is that both our XML
message formats and our java object models are predefined.</P>
<P>If you're reading this, you've probably figured out that the
buzzword for doing this is "data-binding". Most data-binding tools
are strongly unidirectional by nature -- they assume that one side or
the other is predefined and that you can afford to let that side
define the other side - a predefined XML format and generated java
classes, or a predefined java object model and generated XML
structure. From what I can find, Castor started out this way, but
Castor has been around for several years, and by now has pretty good
support for arbitrary-to-arbitrary mapping. Now I just need to figure
out how to make it work.</P>
<H2>Complex Examples</H2>
<P>The first problem I ran into was that there seems to be a shortage
of well-documented examples of solving complex problems with
Castor. The Castor docs at castor.codehaus.org are pretty good, and
there are some excellent intro articles out there, my favorites are:
Professional XML - Chapter 15: Data Binding
http://www.codeproject.com/books/wrox_5059w.asp?print=true
"XML and Java technologies: Data binding with Castor"
http://www-128.ibm.com/developerworks/xml/library/x-bindcastor/
by Dennis Sosnoski
"XML Data Binding with Castor"
http://www.onjava.com/pub/a/onjava/2001/10/24/xmldatabind.html
by Dion Almaer in JavaWorld.
My first few experiments went fine, but when it came time to tackle
mapping a real, existing XML message format to an existing Java object
model, I ran into problems.
The Simple Example
I'm afraid I can't show you the real XML and classes (client
confidentiality and all that), but I mocked up a test case with a
similar structuer. In this test case, we have an Order message that
contains a Customer and a list of Items. So, obviously enough, we
have an <Order> tag set and inside it we have a <Customer> tag set,
and one or more <Item> tag sets. Something like this:
<Order>
<Customer>
<Recipient>Bob</Recipient>
<Address>123 Public Street</Address>
<ZipCode>11111111</ZipCode>
</Customer>
<Item>
<Sku>1</Sku>
<Description>soap</Description>
<Manufacturer>Irish Spring</Manufacturer>
</Item>
<Item>
<Sku>2</Sku>
<Description>towel</Description>
<Manufacturer>Sears</Manufacturer>
</Item>
<Item>
<Sku>3</Sku>
<Description>bathrobe</Description>
<Manufacturer>Polo</Manufacturer>
</Item>
<!-- ...repeat Item tags ad nauseam... -->
</Order>
Unfortunately, in the real world, the message format I have to
actually work with has some extra tags and looks like this:
<Order>
<CustomerWrapper>
<WrapperDetail>uselessValue</WrapperDetail>
<Customer>
<Recipient>Bob</Recipient>
<Address>123 Public Street</Address>
<ZipCode>11111111</ZipCode>
</Customer>
</CustomerWrapper>
<Items>
<Item>
<Sku>1</Sku>
<Description>soap</Description>
<Manufacturer>Irish Spring</Manufacturer>
</Item>
<Item>
<Sku>2</Sku>
<Description>towel</Description>
<Manufacturer>Sears</Manufacturer>
</Item>
<Item>
<Sku>3</Sku>
<Description>bathrobe</Description>
<Manufacturer>Polo</Manufacturer>
</Item>
<!-- ...repeat Item tags ad nauseam... -->
</Items>
</Order>
We have some extra, redundant enclosing <CustomerWrapper> tags
and details, and the <Item> tags are grouped in an <Items> tag set.
None of this especially offends me (in fact, I kind of like the
enclosing <Items> tag set) but it does cause some problems when I
tried out my naive Castor mapping. Specifically, it causes Castor to
throw CastorExceptions with error messages like:
"Unable to find FieldDescriptor for 'CustomerWrapper' in ClassDescriptor of
Order{file: [not available]; line: 2; column: 29}"
...and:
"unable to find FieldDescriptor for 'Items' in ClassDescriptor of
Order{file: [not available]; line: 51; column: 10}"
So I followed the old programming adage, "If at first you don't
succeed, try a simpler problem". I started throwing away tags in my
example message until I got a version that worked with my simple
mapping. Here's what I came up with; three mapping files; one for
<Order>, which includes two other files, one for <Customer> and one
for <Item>. The java class files are just really-straightforward java
data objects, instance variables with getters and setters; the Order
class has a Customer instance and an ArrayList of Item instances.
Still, I'm going to include the java source too, so you can compile it
and try it.
For this first cut, first I built a individual mapping files for
the Customer and the Item. They're both pretty simple.
-----Customer Mapping
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="com.foo.Customer">
<map-to xml="Customer"/>
<field name="recipient" type="String" node="text">
<bind-xml name="Recipient"/>
</field>
<field name="address" type="String" node="text">
<bind-xml name="Address"/>
</field>
<field name="zipCode" type="String" node="text">
<bind-xml name="ZipCode"/>
</field>
</class>
</mapping>
-----
-----Item Mapping
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="com.foo.Item">
<field name="sku" type="String" node="text">
<bind-xml name="Sku"/>
</field>
<field name="description" type="String" node="text">
<bind-xml name="Description"/>
</field>
<field name="manufacturer" type="String" node="text">
<bind-xml name="Manufacturer"/>
</field>
</class>
</mapping>
-----
-----Order Mapping
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<include href="CustomerMapping.xml"/>
<include href="ItemMapping.xml"/>
<class name="com.foo.Order">
<field name="customer" type="com.foo.Customer">
<xml bind-to="Customer"/>
</field>
<field name="Items" type="com.foo.Item" collection="arraylist"/>
</class>
</mapping>
-----
And here's the java source:
-----com/foo/Order.java
package com.foo ;
import java.util.Iterator ;
import java.util.List ;
import java.util.ArrayList ;
public class Order {
private Customer instanceCustomer ;
public Customer getInstanceCustomer() {
return this.instanceCustomer ;
}
public void setInstanceCustomer(Customer customer) {
this.instanceCustomer = customer ;
}
private List instanceItems ;
public List getInstanceItems() {
return this.instanceItems ;
}
public void setInstanceItems(List items) {
this.instanceItems = items ;
}
// Singular version for Castor
public List getInstanceItem() {
return this.instanceItems ;
}
public String toString() {
StringBuffer buf = new StringBuffer() ;
buf.append("" + getInstanceCustomer()) ;
if (null == getInstanceItems()) {
buf.append("Items list is null.") ;
return buf.toString() ;
}
if (getInstanceItems().isEmpty()) {
buf.append("Items list is empty.") ;
return buf.toString() ;
}
Iterator it = getInstanceItems().iterator() ;
while (it.hasNext()) {
buf.append("----\n") ;
buf.append("" + (Item)it.next()) ;
}
return buf.toString() ;
}
}
-----
-----/com/foo/Customer.java
package com.foo ;
public class Customer {
private String instanceRecipient ;
private String instanceAddress ;
private String instanceZipCode ;
public String getInstanceRecipient() {
return this.instanceRecipient ;
}
public void setInstanceRecipient(String recipient) {
this.instanceRecipient = recipient ;
}
public String getInstanceAddress() {
return this.instanceAddress ;
}
public void setInstanceAddress(String address) {
this.instanceAddress = address ;
}
public String getInstanceZipCode() {
return this.instanceZipCode ;
}
public void setInstanceZipCode(String zipCode) {
this.instanceZipCode = zipCode ;
}
public String toString() {
return "Recipient: " + getInstanceRecipient() + "\n"
+ "Address: " + getInstanceAddress() + "\n"
+ "ZipCode: " + getInstanceZipCode() + "\n" ;
}
}
-----
-----/com/foo/Item.java
package com.foo ;
public class Item {
private String instanceSku ;
private String instanceDescription ;
private String instanceManufacturer ;
public String getInstanceSku() {
return this.instanceSku ;
}
public void setInstanceSku(String sku) {
this.instanceSku = sku ;
}
public String getInstanceDescription() {
return this.instanceDescription ;
}
public void setInstanceDescription(String description) {
this.instanceDescription = description ;
}
public String getInstanceManufacturer() {
return this.instanceManufacturer ;
}
public void setInstanceManufacturer(String manufacturer) {
this.instanceManufacturer = manufacturer ;
}
public String toString() {
return "Sku: " + getInstanceSku() + "\n"
+ "Description: " + getInstanceDescription() + "\n"
+ "Manufacturer: " + getInstanceManufacturer() + "\n" ;
}
}
-----
And now the class to invoke it all:
-----/com/foo/UnmarshallTest.java
package com.foo ;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Iterator;
import java.util.List ;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.ValidationException;
public class UnmarshallTest {
public static void main(String[] args) {
String rootDir = "./castortest/working/" ;
String dataFileName = "OrderExample.xml" ;
String mappingFileName = "OrderMapping.xml" ;
if (0 < args.length) {
rootDir = args[0] ;
}
UnmarshallTest test = new UnmarshallTest() ;
test.test(rootDir + dataFileName, rootDir + mappingFileName) ;
}
public void test(String dataFileName, String mappingFileName) {
try {
File dataFile = new File(dataFileName) ;
Reader dataReader = new FileReader(dataFile) ;
Mapping testMapping= new
org.exolab.castor.mapping.Mapping();
System.out.println("Reading sample data from \"" +
dataFileName + "\"") ;
System.out.println("Loading mapping from \"" +
mappingFileName +
"\"") ;
testMapping.loadMapping(mappingFileName);
Unmarshaller unmarshaller = new
Unmarshaller(Order.class);
unmarshaller.setMapping(testMapping);
Order order =
(Order)(unmarshaller.unmarshal(dataReader));
System.out.println("" + order) ;
} catch (MarshalException e) {
e.printStackTrace() ;
throw new RuntimeException("Castor Marshal Exception") ;
} catch (MappingException e) {
e.printStackTrace();
throw new RuntimeException("Castor Mapping Exception") ;
} catch (ValidationException e) {
e.printStackTrace() ;
throw new RuntimeException("XML Validation Exception") ;
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("IOexception") ;
}
}
}
-----
So now I have to figure out how to make this work with the full
version of the data. I have three problems:
1) Ignoring the <CustomerWrapper> tag set
2) Ignoring the <WrapperDetail> tag.
3) Mapping the <Items> tag set.
I tried a lot of things. For various reasons, I ended up dealing
with the <WrapperDetail> tag first.
Turns out there's a setting on the Castor Unmarshaller to ignore extra
tags. Oddly enough, you can set it with the mysteriously-named
Unmarshaller.setIgnoreExtraElements(). There's also a
Unmarshaller.setIgnoreExtraAttributes(). This fixed the
<WrapperDetail> problem, but it also meant that Castor happily ignored
my other mistakes instead of throwing an error, so I didn't really
like it.
Eventually the good folks on the Castor-User's list pointed me in the
direction of the <field> tag's transient="true" attribute:
<field name="WrapperDetail" type="string" transient="true">
<bind-xml name="WrapperDetail"/>
</field>
With transient set to "true", Castor now knows about the <WrapperDetail>
<field> and knows that it doesn't need to map it to an instance variable.
It still seems to need a name attribute on <field>, even though that name
doesn't match any instance variable. With this version, I have the
following files for the Order mockup and Order mappnig (the other
files stay the same as above, so I'm not going to repeat them):
-----
<Order>
<WrapperDetail>uselessValue</WrapperDetail>
<Customer>
<Recipient>Bob</Recipient>
<Address>123 Public Street</Address>
<ZipCode>11111111</ZipCode>
</Customer>
<Item>
<Sku>1</Sku>
<Description>soap</Description>
<Manufacturer>Irish Spring</Manufacturer>
</Item>
<Item>
<Sku>2</Sku>
<Description>towel</Description>
<Manufacturer>Sears</Manufacturer>
</Item>
<Item>
<Sku>3</Sku>
<Description>bathrobe</Description>
<Manufacturer>Polo</Manufacturer>
</Item>
<!-- ...repeat Item tags ad nauseam... -->
</Order>
-----
-----
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<include href="CustomerMapping.xml"/>
<include href="ItemMapping.xml"/>
<class name="com.foo.Order">
<field name="WrapperDetail" type="string" transient="true">
<bind-xml name="WrapperDetail"/>
</field>
<field name="instanceCustomer" type="com.foo.Customer">
<xml bind-to="Customer"/>
</field>
<field name="Item" type="com.foo.Item" collection="arraylist" />
</class>
</mapping>
-----
Dealing with the <CustomerWrapper> tag set was much more frustrating,
though when I finally figured it out it was really easy - really
weird, but easy. Now remember, the original version we have simply
has a <Customer> tag set that contains <Recipient>, <Address> and
<ZipCode>, so my original <Customer> mapping was pretty
straightforward.
The Castor-Users folks pointed me in the direction of the location
attribute on the <xml> element. This is sort of like putting a prefix
on the path of the XML tag name. You can't actually put the path on
the XML tag name itself, which would be kinda handy (I wonder how hard
it would be to add that...). But what you can do is put the extra
location attribute on. I tried the obvious approach, by adding the
location tag to the <Order> mapping, trying to tell it that the
<Customer> field was inside <CustomerWrapper>:
-----
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<include href="CustomerMapping.xml"/>
<include href="ItemMapping.xml"/>
<class name="com.foo.Order">
<field name="instanceCustomer" type="com.foo.Customer">
<xml bind-to="CustomerTag" location="CustomerWrapperTag"/>
</field>
<field name="instanceItems" type="com.foo.Item" container="false"
collection="arraylist">
<bind-xml name="ItemsTag"/>
</field>
</class>
</mapping>
-----
For some reason this didn't work, though it still seems (to me at
least) like the obvious way to do it.
I also tried to move it inside the <Customer> mapping, again in what
seemed to me the obvious way:
-----
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="com.foo.Customer">
<map-to xml="CustomerTag"/>
<field name="WrapperDetail" type="string" transient="true">
<bind-xml name="WrapperDetailTag"/>
</field>
<field name="instanceRecipient" type="string" node="text">
<bind-xml name="RecipientTag"
location="CustomerWrapperTag/CustomerTag"/>
</field>
<field name="instanceAddress" type="string" node="text">
<bind-xml name="AddressTag"
location="CustomerWrapperTag/CustomerTag"/>
</field>
<field name="instanceZipCode" type="string" node="text">
<bind-xml name="ZipCodeTag"
location="CustomerWrapperTag/CustomerTag"/>
</field>
</class>
</mapping>
-----
Again, this didn't work. I think there's something I'm just not
getting right, in my head, about how the scoping works here; I don't
have a good intuitive feel for the way the focal point of the context
moves around as the XML data is parsed and mapped. I suspect this is
the same reason I hate doing XSLT.
I'm not sure how the heck I stumbled onto the real solution; I think I
just sat down and worked through every combination until I found one
that worked (I *hate* doing this, by the way). What actually worked
is sort of out of left field; I mapped the Customer object to the
<CustomerWrapper> tag set, and then put a location="CustomerTag" on
each field inside <CustomerWrapper>:
-----
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="com.foo.Customer">
<map-to xml="CustomerWrapperTag"/>
<field name="WrapperDetail" type="string" transient="true">
<bind-xml name="WrapperDetailTag"/>
</field>
<field name="instanceRecipient" type="string" node="text">
<bind-xml name="RecipientTag" location="CustomerTag"/>
</field>
<field name="instanceAddress" type="string" node="text">
<bind-xml name="AddressTag" location="CustomerTag"/>
</field>
<field name="instanceZipCode" type="string" node="text">
<bind-xml name="ZipCodeTag" location="CustomerTag"/>
</field>
</class>
</mapping>
-----
My final problem was figuring out how to map the <Items></Items> tags.
I think part of why this ended up being a real pain to figure out was
because of Castor's default/introspection mapping behavior. Castor
can do some pretty clever things on its own, to figure out what should
go where. Unfortunately, when *you* are trying to figure out
precisely how the mapping tags work, Castor's helpful behavior can
lead to really mysterious behavior.
When I finally realized that maybe I never had gotten it right to
begin with, and Castor was simply mapping the multiple <Item> tag sets
using default mappings, I went back and, again, started methodically
working through every permutation to see when the field would work
right.
Somewhere along the line, somebod on Castor-Users noticed that I'd
forgotten to include a <map-to> element in my Item mapping. I think
this may be a good example of Castor's automatic mapping confusing me
by Doing The Right Thing, so I'm busy playing with the shiny dials
that aren't connected to anything while Castor is just chugging along
and mapping data. So I added one:
-----
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="com.foo.Item">
<map-to xml="ItemTag"/>
<field name="instanceSku" type="string" node="text">
<bind-xml name="SkuTag"/>
</field>
<field name="instanceDescription" type="string" node="text">
<bind-xml name="DescriptionTag"/>
</field>
<field name="instanceManufacturer" type="string" node="text">
<bind-xml name="ManufacturerTag"/>
</field>
</class>
</mapping>
-----
I think this helped a bit, but what really made the difference was
when the folks on Castor-Users clued me in to the new feature, the
<field container="false"> attribute for the Order mapping:
-----
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<include href="CustomerMapping.xml"/>
<include href="ItemMapping.xml"/>
<class name="com.foo.Order">
<field name="instanceCustomer" type="com.foo.Customer">
<xml bind-to="CustomerTag"/>
</field>
<field name="instanceItems" type="com.foo.Item" container="false"
collection="arraylist">
<bind-xml name="ItemsTag"/>
</field>
</class>
</mapping>
-----
This finally did the trick, and now it runs. The java classes have
stayed the same all through this, so I won't repeat them here, but
let's put all of the XML right here in one spot:
-----
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="com.foo.Customer">
<map-to xml="CustomerWrapperTag"/>
<field name="WrapperDetail" type="string" transient="true">
<bind-xml name="WrapperDetailTag"/>
</field>
<field name="instanceRecipient" type="string" node="text">
<bind-xml name="RecipientTag" location="CustomerTag"/>
</field>
<field name="instanceAddress" type="string" node="text">
<bind-xml name="AddressTag" location="CustomerTag"/>
</field>
<field name="instanceZipCode" type="string" node="text">
<bind-xml name="ZipCodeTag" location="CustomerTag"/>
</field>
</class>
</mapping>
-----
-----
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="com.foo.Item">
<map-to xml="ItemTag"/>
<field name="instanceSku" type="string" node="text">
<bind-xml name="SkuTag"/>
</field>
<field name="instanceDescription" type="string" node="text">
<bind-xml name="DescriptionTag"/>
</field>
<field name="instanceManufacturer" type="string" node="text">
<bind-xml name="ManufacturerTag"/>
</field>
</class>
</mapping>
-----
-----
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<include href="CustomerMapping.xml"/>
<include href="ItemMapping.xml"/>
<class name="com.foo.Order">
<field name="instanceCustomer" type="com.foo.Customer">
<xml bind-to="CustomerTag"/>
</field>
<field name="instanceItems" type="com.foo.Item" container="false"
collection="arraylist">
<bind-xml name="ItemsTag"/>
</field>
</class>
</mapping>
-----
-----
<OrderTag>
<CustomerWrapperTag>
<WrapperDetailTag>uselessValue</WrapperDetailTag>
<CustomerTag>
<RecipientTag>Bob</RecipientTag>
<AddressTag>123 Public Street</AddressTag>
<ZipCodeTag>11111111</ZipCodeTag>
</CustomerTag>
</CustomerWrapperTag>
<ItemsTag>
<ItemTag>
<SkuTag>1</SkuTag>
<DescriptionTag>soap</DescriptionTag>
<ManufacturerTag>Irish Spring</ManufacturerTag>
</ItemTag>
<ItemTag>
<SkuTag>2</SkuTag>
<DescriptionTag>towel</DescriptionTag>
<ManufacturerTag>Sears</ManufacturerTag>
</ItemTag>
<ItemTag>
<SkuTag>3</SkuTag>
<DescriptionTag>bathrobe</DescriptionTag>
<ManufacturerTag>Polo</ManufacturerTag>
</ItemTag>
<!-- ...repeat Item tags ad nauseam... -->
</ItemsTag>
</OrderTag>
-----
And that's it!
Next lesson, they threw me a curve ball and it turns out I need to
have a separate Items object with a few extra bits of state and some
convenience method.
Steven J. Owens
[EMAIL PROTECTED]
-------------------------------------------------
If you wish to unsubscribe from this list, please
send an empty message to the following address:
[EMAIL PROTECTED]
-------------------------------------------------