Posts Tagged Xml

XML Serialization

Another pair of those seemingly forgotten classes in the core Java API are the java.beans.XMLEncoder and java.beans.XMLDecoder. Added in 1.4, they don’t really seem to be used all that much, that I have come across anyway. It seems that whenever someone needs to convert objects into xml, they instantly reach for 3rd-party libraries rather than looking in the core API. Not to downplay other APIs, but I do think XMLEncoder and XMLDecoder deserve consideration, especially when you simply need to export data objects in a simple and repeatable manner.

First off, we need something to work with, how about a nice Person class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Person {
    private String firstName, lastName;
    private short age;

    public Person(){ super(); }

    public Person(final String firstName, final String lastName, final short age){
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    public String getFirstName() { return firstName; }

    public void setFirstName(final String firstName) { this.firstName = firstName; }

    public String getLastName() { return lastName; }

    public void setLastName(final String lastName) { this.lastName = lastName; }

    public short getAge() { return age; }

    public void setAge(final short age) { this.age = age; }

    @Override
    public String toString() { return firstName+" "+lastName+" ("+age+")"; }
}

It’s nothing special, just first name, last name and age with getters and setters. I also overrode the toString() method so we can get something useful from it.

Now say you are in a situation where you need to quickly convert objects of the Person class to a format usable by another client (maybe another programming language); as much trouble as XML can be a times, it is still a good interoperability choice. Converting a collection of Person objects to XML is trivial:

1
2
3
4
5
6
7
8
final Collection<Person> people = new LinkedList<Person>();
people.add( new Person("Joe","Smith",(short) 32) );
people.add( new Person("Abe","Ableman",(short) 54) );
people.add( new Person("Cindy","Lindy",(short) 42) );

final XMLEncoder encoder = new XMLEncoder( outputStream );
encoder.writeObject(people);
encoder.close();

Which will generate the following XML in the given output stream:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.6.0_13" class="java.beans.XMLDecoder">
 <object class="java.util.LinkedList">
  <void method="add">
   <object class="foo.Person">
    <void property="age">
     <short>32</short>
    </void>
    <void property="firstName">
     <string>Joe</string>
    </void>
    <void property="lastName">
     <string>Smith</string>
    </void>
   </object>
  </void>
  <void method="add">
   <object class="foo.Person">
    <void property="age">
     <short>54</short>
    </void>
    <void property="firstName">
     <string>Abe</string>
    </void>
    <void property="lastName">
     <string>Ableman</string>
    </void>
   </object>
  </void>
  <void method="add">
   <object class="foo.Person">
    <void property="age">
     <short>42</short>
    </void>
    <void property="firstName">
     <string>Cindy</string>
    </void>
    <void property="lastName">
     <string>Lindy</string>
    </void>
   </object>
  </void>
 </object>
</java>

Yeah, it’s not pretty and it’s a bit on the verbose side, but it was easy to produce and easy to read. Now, if you want to read that back into Java objects, it it equally as simple:

1
2
3
final XMLDecoder decoder = new XMLDecoder( inputStream );
final Collection<Person> persons = (Collection<Person>)decoder.readObject();
decoder.close();

which will give you back copies of your original objects. If you were to call toString() on the resulting collection you would get:

[Joe Smith (32), Abe Ableman (54), Cindy Lindy (42)]

Exactly what you put in.

Obviously, this is not the best approach for serializing and deserializing data, but it’s another tool in the tool box and I am always surprised by how few developers actually know that they are avaialable.

Popularity: 1% [?]

  • Share/Bookmark

Tags: , ,

Embedding Jetty 6 in Spring

A few years ago, I wrote a blog entry about Embedding Jetty in Spring. It became quite popular, at least in relation to other pages on my site. Unfortunately, as I noted in the header of that posting, it has become a bit out-dated as newer versions of Jetty have been released. Well, with a little prodding via emails and a handful of free time, I have come up with an updated version for Jetty 6.1.11 and Spring 2.5.5 that requires no additional helper-classes.

For simplicity I came up with a spring context for embedding Jetty based on the example included with Jetty which replicates the default full configuration, LikeJettyXml.java. This seemed a good place to start since you will either need all of that, or slightly less… and removing stuff is simple.

For the most part the spring context configuration mirrors the java source from the example; the only real deviation comes form the life-cycle addition method calls addLifeCycle(), which spring does not directly support. To perform that one missing dependency injection, you can use the spring MethodInvokingFactoryBean to create a bean and then call a method to inject it into the target bean.

1
2
3
4
5
6
7
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject" ref="server.Server" />
        <property name="targetMethod" value="addLifeCycle" />
        <property name="arguments">
            <list><ref local="server.ContextDeployer" /></list>
        </property>
    </bean>

which simply calls the addLifeCycle method on the server instance to add the two deployer instances.

The whole context file reads as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        <bean id="server.Server" class="org.mortbay.jetty.Server" destroy-method="stop">
            <property name="threadPool">
                <bean class="org.mortbay.thread.QueuedThreadPool">
                    <property name="maxThreads" value="100" />
                </bean>
            </property>
            <property name="connectors">
                <list>
                    <bean class="org.mortbay.jetty.nio.SelectChannelConnector">
                        <property name="port" value="8080" />
                        <property name="maxIdleTime" value="30000" />
                    </bean>
                </list>
            </property>
            <property name="handler">
                <bean class="org.mortbay.jetty.handler.HandlerCollection">
                    <property name="handlers">
                        <list>
                            <ref local="server.ContextHandlerCollection" />
                            <bean class="org.mortbay.jetty.handler.DefaultHandler" />
                            <bean class="org.mortbay.jetty.handler.RequestLogHandler">
                                <property name="requestLog">
                                    <bean class="org.mortbay.jetty.NCSARequestLog">
                                        <constructor-arg value="cfg/logs/jetty-yyyy_mm_dd.log" />
                                        <property name="extended" value="false"/>
                                    </bean>
                                </property>
                            </bean>
                        </list>
                    </property>
                </bean>
            </property>

            <property name="userRealms">
                <list>
                    <bean class="org.mortbay.jetty.security.HashUserRealm">
                        <property name="name" value="Test Realm" />
                        <property name="config" value="cfg/etc/realm.properties" />
                    </bean>
                </list>
            </property>

            <property name="stopAtShutdown" value="true" />
            <property name="sendServerVersion" value="true"/>
        </bean>

        <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="targetObject" ref="server.Server" />
            <property name="targetMethod" value="addLifeCycle" />
            <property name="arguments">
                <list><ref local="server.ContextDeployer" /></list>
           </property>
        </bean>

        <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="targetObject" ref="server.Server" />
            <property name="targetMethod" value="addLifeCycle" />
            <property name="arguments">
                <list><ref local="server.WebAppDeployer" /></list>
           </property>
        </bean>

        <bean id="server.ContextHandlerCollection" class="org.mortbay.jetty.handler.ContextHandlerCollection" />

        <bean id="server.ContextDeployer" class="org.mortbay.jetty.deployer.ContextDeployer">
            <property name="contexts" ref="server.ContextHandlerCollection" />
            <property name="configurationDir">
                <bean class="org.mortbay.resource.FileResource">
                    <constructor-arg value="file://./cfg/contexts" />
                </bean>
            </property>
            <property name="scanInterval" value="1" />
        </bean>

        <bean id="server.WebAppDeployer" class="org.mortbay.jetty.deployer.WebAppDeployer">
            <property name="contexts" ref="server.ContextHandlerCollection" />
            <property name="webAppDir" value="cfg/webapps" />
            <property name="parentLoaderPriority" value="false" />
            <property name="extract" value="true" />
            <property name="allowDuplicates" value="false" />
            <property name="defaultsDescriptor" value="cfg/etc/webdefault.xml" />
        </bean>
    </beans>

In order to start the server you need to be sure that the MethodInvokingFactoryBeans have been fired before the server has been started; the easiest way is to start the server with an external class once the context has loaded.

1
2
3
4
5
6
7
8
    public class Main {
        public static void main(String[] args) throws Exception {
            ApplicationContext context = new FileSystemXmlApplicationContext("cfg/server-context.xml");
            Server server = (Server)context.getBean("server.Server");
            server.start();
            server.join();
        }
    }

This should be a good starting point and general template for anything you need… just inject or modify whatever configuration setup you need for your application. I have attached the zipped up Eclipse project I used to create and run this test. You will need the following jars somewhere on your classpath: ant-1.6.5.jar, commons-el-1.0.jar, commons-logging.jar, jasper-compiler-5.5.15.jar, jasper-runtime-5.5.1.5.jar, jetty-6.1.11.jar, jetty-util-6.1.11.jar, jsp-api-2.0.jar, servlet-api-2.5-6.1.11.jar, and spring.jar… all of which can be found in the lib directories of the Spring and Jetty distributions uses.

Good luck. Enjoy, and feel free to let me know if I did something stupid, missed something or if there is any area of this you want me to cover in greater detail or in a different use case.

Popularity: 100% [?]

  • Share/Bookmark

Tags: ,

Titles as CDATA

When you are generating XML, you should be sure that “free-text” items such as text content or titles (or even people’s names) are in text elements as CDATA (non-parsed character data) This ensures that any special characters or content will not break the validity of the XML document.

1
2
3
<user>
    <name><![CDATA[Matthew O'Brien]]></name>
</user>

I have come across bugs (even some that I created) related to this issue, especially when the text is in an element attribute, rather than a text element.

1
<user name="Matthew O'Brien" />

Though this is acceptable, it could lead to problems if the attribute could ever have quotes in it or if your XML implementation happens to render the XML with single quotes:

1
<user name='Matthew O'Brien' />

This would fail an attempt to re-parse the serialized XML document.

Popularity: unranked [?]

  • Share/Bookmark

Tags: ,

Switch to our mobile site