Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Wednesday, August 1, 2012

Running Tomcat on HTTPS with a Secure Certificate on Dreamhost

From http://www.appfoundation.com/2012/08/running-tomcat-on-https-with-a-secure-certificate-on-dreamhost/:


1. Create a new domain
  • Login to your Dreamhost account
  • Go to "Manage Domains"
  • Select to "Add New Domain"
  • Make sure to specify the "Run this domain under the user" as a user associated with the FTP account on your private server. This results in the new domain being installed on your private server
  • Wait for the new domain to be active, which can take up to 15 minutes. Once you can hit the new domain in your browser you are ready to go.

2. Add SSL to the domain
  • Login to your Dreamhost account
  • Go to "Manage Domains"
  • Next to your domain press the "Add" button in the "Secure Hosting" column
  • Fill out the admin information for your domain
  • Wait for the SSL version of your domain to be available, which can take up to 15 minutes. Once you can hit the HTTPS version of your domain you are ready to go.
  • This service costs $5/month

3. Add a professionally signed secure certificate to the domain
  • This is required if you don't want your users to get the "Not a secure site" warning page when going to the HTTPS version of your site
  • Login to your Dreamhost account
  • Under "Domains" go to "Secure Hosting"
  • Select to "Add Secure Certificate"
  • Fill out the information and order the certificate
  • Wait up to 15 minutes for the certificate to be complete. Once the status of the certificate request on the "Secure Hosting" page is "OK" you are ready to go.
  • This service costs $15.00

4. Fix Dreamhost overwrites
  • The addition of this service will cause your private server to have some of its components refreshed, depending on what things you have selected to have managed on the Private Server dashboard
  • The most common issue is the refresh of the MySQL configuration file, in which the default version doesn't allow lower case table names
  • You know this is happening when you start getting database errors about table names not being found
  • Modify MySQL to not use case sensitive names, which means you have to edit /dh/mysql/mysql/my.cnf
  • Add lower_case_table_names=1 AFTER the [mysqld] tag. If you add this param before, it does not get picked up by the startup script.
## my.cnf for ds3241:mysql (generated)
#
# put local parameters in my.cnf.local for inclusion below
#
### useful info:
# socket = /dh/mysql/mysql/mysql.mysql.sock
# backup user = root
# backup pass = PASSWORD_HERE
[mysqld_safe]
open_files_limit = 65535
[mysqld]
# options without values
lower_case_table_names=1
log-warnings
new

5. Change Apache ports (if you are running Tomcat HTTP on 80 and Tomcat HTTPS on 443)
  • If you are running Tomcat on port 80 and intending on running HTTPS on 443, you will need to tell Apache to run somewhere else
  • You have to edit /usr/local/dh/apache2/httpd-argon/etc/httpd.conf and change all of the port :443's to :8443's

6. Restart the server
  • Login to your Dreamhost account
  • Go to "Dedicated Servers" and then "Dashboard"
  • Press the "Restart" button next to your private server
  • Wait for about 5 minutes for the server to come back up

7. Install tools needed for generating a certificate for Tomcat

8. Generate a keystore for Tomcat using your certificate
  • This took me a really long time to figure out. http://www.brandonchecketts.com/archives/convert-and-openssl-apache-ssl-certificate-to-a-pkcs12-tomcat contained the missing pieces.
  • In Dreamhost go to Secure Hosting and select the "View" button next to your Secure Certificate for your domain
  • This will show you 3 keys: Certificate, Private Key, and Intermediate Certificate
  • Copy and paste the Certificate into a file named "yourdomain.com.crt"
  • Copy and paste the Intermediate Certificate into a file named "yourdomain.com.int"
  • Copy and paste the Private Key into a fie named "yourdomain.com.key"
  • Run the following at the Windows Command Line:
openssl pkcs12 -export -in yourdomain.com.crt -out yourdomain.com.pkcs12 -name "yourdomain.com" -inkey yourdomain.com.key
  • Pick a password for the keystore, which you will need to put in the Tomcat server.xml in the future
  • This generates a file called yourdomain.com.pkcs12

9. Upload the keystore
  • Upload the yourdomain.com.pkcs12 to the conf directory of your Tomcat installation on your private server
  • For example our Tomcat conf directory is /opt/apache-tomcat-7.0.27/conf

10. Modify the Tomcat server.xml
  • First you have to set the redirect to the HTTP port from the perspective of the HTTPS port. Note that all you need here is the redirect port, everything else
is specific to an ExtJS GZIP optimization
Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="443"
compression = "on"
compressableMimeType="text/html,text/xml,text/css,text/javascript,application/x-javascript,image/jpeg,image/png,image/gif"
compressionMinSize="1"
  • Next you have to add a connector configuration for HTTPS that references the keystore:
Connector port="443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="conf/yourdomain.com.pkcs12" keystoreType= "PKCS12" keystorePass="YOUR_PASSWORD"
compression = "on"
compressableMimeType="text/html,text/xml,text/css,text/javascript,application/x-javascript,image/jpeg,image/png,image/gif"
compressionMinSize="1"

  • Finally you have to modify the AJP connector:
Connector port="8009" protocol="AJP/1.3" redirectPort="443" 

11. Startup your Tomcat instance
  • From the Tomcat directory run ./bin/startup.sh and make sure logs/catalina.out doesn't contain any errors related to the keystore
  • The HTTPS version of your site should now be running using your certificate, which means there won't be a security warning page when visiting the site

Tuesday, August 23, 2011

Using DBUnit through Ant with a large MS SQL Database

The following describes my experiences with using DBUnit through Ant with a large MS SQL database, and all of the adventures I had. The intention is to use DBUnit though Ant to export a database as an XML file, to clear the database, and to re-populate it using that XML file.

Why?

Automated acceptance testing. You start with an XML backup of what you want your base system to contain, refresh your database with that backup, run your suite of automated acceptance tests, and then restore the XML backup again. That way your tests can do things specific to the application like create users, invoices, etc, and otherwise mess around with every conceivable operation. When you are done you can restore everything to the way it was before.

Running it though Ant lets you easily integrate this refresh into other automation tools such as Jenkins (Hudson), TeamCity, and so on.

Did you say large?

Yes I did, but in this context I am using it to refer to a database with a large number of tables, not a large number of records. A large number of tables is a catch-all for several circumstances which you cause you trouble, such as with tables which have keywords for names, foreign key dependencies between tables, tables with timestamp columns, and some more items I will get address.

But I want a lot of records!

There are a couple of problems with using a lot of records with DBUnit, the first of which is that if you are exporting and importing you will have to deal with very long save and load times. As a general rule you probably are aiming for an export XML file size of under 3 MB, which is around 2,000 records depending on what it is your tables. The second problem has to do with memory, but I have a workaround for that as well.

DBUnit with Ant

So you want to use Ant? You are then going to have to write your own task by extending the DBUnit Ant Task. This is because there are options that need to be set [at least to my knowledge] that cannot be set in the DBUnit Ant Task that is provided to you.

To start out you will need to extend three things:

  1. The DbUnitTask, which is the <dbunit> tag. This allows you to define your own Export and Operation tags.
  2. The Export, which is the <export> tag. This is for changing how the export works.
  3. The Operation, which is the <operation> tag. This is for changing how the insert works.

MSDBUnitTask.java

public class MSDBUnitTask extends DbUnitTask {

    @SuppressWarnings("unchecked")
    public void addMSExport(MSExport export) {
        this.getSteps().add(export);
    }
    @SuppressWarnings("unchecked")
    public void addMSOperation(MSOperation operation) {
        this.getSteps().add(operation);
    }
}

MSExport.java

public class MSExport extends Export   {

    private String tableNames;
    @Override
    public void execute(IDatabaseConnection arg0) throws DatabaseUnitException {
        // code goes here
    }

    public String getTableNames() {
        return tableNames;
    }

    public void setTableNames(String tableNames) {
        this.tableNames = tableNames;
    }

}

MSOperation.java

public class MSOperation extends Operation   {

        @Override
    public void execute(IDatabaseConnection arg0) throws DatabaseUnitException {
        // code goes here
    }

}

Including your custom task in Ant

This assumes that the Ant classes from above are compiled into a JAR and present in the ${lib.dir} along with the dbunit.jar

<path id="classpath">

    <fileset dir="${lib.dir}">
        <include name="*.jar" />
    </fileset>   
</path>

<taskdef name="msdbunit" classname="com.foo.ant.dbunit.MSDBUnitTask" classpathref="classpath" />

 

Adventures in Exporting

The Java code here goes in the execute method of MSExport.

This is where the majority of problems start. As it turns out you can export with ease, but can end up with XML that had invalid characters and some other interesting things that cannot be imported.

Let’s start with the result we want (and need): the XML to export the database:

<target name="db-export">

    <msdbunit
        driver="${db.driver}" 
        url="${db.url}" 
        userid="${db.username}" 
        password="${db.password}">

        <dbconfig>
            <property name="escapePattern" value="[?]" /> 
            <property name="datatypeFactory" value="org.dbunit.ext.mssql.MsSqlDataTypeFactory" /> 
        </dbconfig>
        <msexport format="xml" tableNames="${insert.table.names}" dest="full_export.xml" />
    </msdbunit>

</target>

The msdbunit and msexport tags were explained earlier, and the base Java code for creating them was given.

Issue #1: Export Table Order

You must specify the order of tables to do the export in insertion order. This is done by passing in a comma separated list of tables name from ${insert.table.names} into the <msexport> tag as the tableNames attribute.

The reason for this order is that you must take into account FK dependencies when both inserting and deleting. For example if Foo.A is a FK to Bar.A, then you can’t start out by inserting records into Foo; you have to insert into Bar first. From the deletion perspective it is the opposite: You can’t delete from Bar since Foo can reference it and cause the delete to fail.

The MSExport task already has tableNames defined, so in order to get the result as a list of tables all that needs to be done is this:

String[] tableNamesArray = tableNames.split(",");

ITableFilter filter = new SequenceTableFilter(tableNamesArray);

IDataSet dataset = new FilteredDataSet(filter, arg0.createDataSet());

The second part of this is do use this array to define the table sequence. You must do this manually because even though it can be done automatically, with 100+ tables it will take minutes as opposed to a fraction of a second.

Issue #2: Columns that are of type TIMESTAMP or default DATETIME

There is an issue if you export a column of the TIMESTAMP type or of the defaulted DATETIME, because when you try and import it you will get an error regarding not being able to insert a value into the column.

The exact error you get is the following:

Cannot insert an explicit value into a timestamp column

The solution is not to export these columns, which requires the use of a column filter. This was the primary reason for abandoning the default Ant task, as I couldn’t figure out a good way to do a large column filtering.

List<ITable> updatedTables = new ArrayList<ITable>();

for (String tableName : tableNamesArray) {
                ITable table = dataset.getTable(tableName);
                List<Column> excludedColumns = new ArrayList<Column>();
                Column[] columns = table.getTableMetaData().getColumns();
                //For each column...
                for (Column column : columns) {
                    //TIMESTAMP columns have to be ignored in MSSQL because they cannot have an explicit insert
                    //Unfortunately DATETIME in MSSQL also shows here as a TIMESTAMP so the only way to
                    //determine defaulted DATETIME or TIMESTAMP is by looking for defaults and the SQL Type name
                    if ( (column.getDefaultValue() != null && column.getDataType() == DataType.TIMESTAMP) ||
                            column.getSqlTypeName().equals("timestamp")) {
                        excludedColumns.add(column);
                    }
                }
                //convert the list of excluded columns to an array
                Column[] excluded =  excludedColumns.toArray(new Column[excludedColumns.size()]);
                //create a new ITable that excludes the filtered columns
                ITable filteredTable = DefaultColumnFilter.excludedColumnsTable(table,excluded);
                //keep track of the modified table
                updatedTables.add(filteredTable);               
            }
            //Take all of the modified table and create a new dataset
            ITable[] updateTableArray =  updatedTables.toArray(new ITable[updatedTables.size()]);
            CompositeDataSet compositeDataSet = new CompositeDataSet(updateTableArray);

This code iterates over all of the tables, looks for datetime and timestamp columns, excludes them from that table, and adds them to a new list of tables with the appropriate columns excluded. It then constructs a new composite dataset containing all of the new tables and columns.

Issue #3: Exporting your customizations

When you extend the Export task, you have to then define how to export.

//Get the format and file destination
String format = this.getFormat();
File dest = this.getDest();
//Output based on the format
if (format.equalsIgnoreCase(FORMAT_CSV)) {
    CsvDataSetWriter.write(compositeDataSet, dest);
else if (format.equalsIgnoreCase(FORMAT_FLAT)) {
    FlatXmlDataSet.write(compositeDataSet, new FileOutputStream(dest));
} else if (format.equalsIgnoreCase(FORMAT_XML)) {
    XmlDataSet.write(compositeDataSet, new FileOutputStream(dest));
} else if (format.equalsIgnoreCase(FORMAT_DTD)) {
    FlatDtdDataSet.write(dataset, new FileOutputStream(dest));
} else {
    throw new DatabaseUnitException(format+" is not a recognized format.");
}

Issue #4: Exporting Tables that have keywords for names

For example User is a keyword, but you can have a table of this name in MS SQL and access it using [User]. In order to deal with this you want to put all table names within [].

This can be configured in the Ant XML using the escapePattern property of dbconfig:

<target name="db-export">

    <msdbunit
        driver="${db.driver}" 
        url="${db.url}" 
        userid="${db.username}" 
        password="${db.password}">

        <dbconfig>
            <property name="escapePattern" value="[?]" /> 
            <property name="datatypeFactory" value="org.dbunit.ext.mssql.MsSqlDataTypeFactory" /> 
        </dbconfig>
        <msexport format="xml" tableNames="${insert.table.names}" dest="full_export.xml" />
    </msdbunit>

</target>

Issue #5: Data Type Factory

In order to be able to insert records into a MS SQL database using DBUnit, you have to specify the data factory to use as a part of the export. If you don’t any time you try and import a result containing a primary key you will get the following error:

Cannot insert explicit value for identity column in table

This issue can also be resolved in the default Ant using the dataTypeFactory property in the dbconfig:

<target name="db-export">

    <msdbunit
        driver="${db.driver}" 
        url="${db.url}" 
        userid="${db.username}" 
        password="${db.password}">

        <dbconfig>
            <property name="escapePattern" value="[?]" /> 
            <property name="datatypeFactory" value="org.dbunit.ext.mssql.MsSqlDataTypeFactory" /> 
        </dbconfig>
        <msexport format="xml" tableNames="${insert.table.names}" dest="full_export.xml" />
    </msdbunit>

</target>

Issue #6: Which table failed to export?

This issue is that when you are exporting your database and something goes wrong, it is not specified which table was the problem. You just get an error message such as “Cannot insert explicit value for identity column in table” and are left to figure out which table may have caused it.

Since we have our own Ant task that iterates over tables, this is pretty easy to do: put a System.out.println with the table name at the start of the loop. That was if something breaks, you know which table caused it.

Deleting from the Database

If you exported correctly this isn’t a problem, and doesn’t require any custom work beyond the escapePattern and data type factory. It should be noted that before inserting into your database you want to clear it using your export file first. The CLEAN_INSERT operation is supposed to do this on insert for you, but in my experience I have never been able to get it work on a large database. It ends up throwing constraint violations, either because of the database, a bug, or my misuse of the operation.

<target name="db-delete">

    <msdbunit
        driver="${db.driver}" 
        url="${db.url}" 
        userid="${db.username}" 
        password="${db.password}">

        <dbconfig>
            <property name="escapePattern" value="[?]" /> 
            <property name="datatypeFactory" value="org.dbunit.ext.mssql.MsSqlDataTypeFactory" /> 
        </dbconfig>
        <operation type=”DELETE_ALL” format="xml" src="full_export.xml" />
    </msdbunit>

</target>

Populating the Database

The Java code here goes in the execute method of MSOperation.

The same as with deleting from the database, if you correctly exported the dataset you should not have any problems. There is one feature though that is nice to have, which is the ability in the case of an error to know which table cause the error.

        //Only perform an operation for every table if doing a clean insert for MS SQL
        if (this.getType().equalsIgnoreCase("MSSQL_CLEAN_INSERT")) {
            try {
                FileReader reader = new FileReader(this.getSrc());
                String format = this.getFormat();
                IDataSet dataset = null;
                if (format.equalsIgnoreCase(FORMAT_XML)) {
                    dataset = new XmlDataSet(reader);
                } else {
                    throw new DatabaseUnitException("xml is the only supported format.");
                }
                String[] tableNames = dataset.getTableNames();
                //For each table, create a new dataset and execute the operation because:
                for (int i = 0; i < tableNames.length; i++) {
                    String tableName = tableNames[i];
                    System.out.println("Populating table "+tableName);
                    ITable table = dataset.getTable(tableName);
                    ITable[] iTables = new ITable[1];
                    iTables[0] = table;
                    CompositeDataSet composite = new CompositeDataSet(iTables);
                    this.getDbOperation().execute(connection, composite);
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new DatabaseUnitException(e);
            }
        } else {
            super.execute(connection);
        }

    }

Beyond that all you need is the following:

  • MSSQL_CLEAN_INSERT
  • The MS SQL Data Type Factory
  • The escapePattern for tables with keywords for names

<target name="db-populate">

    <msdbunit
        driver="${db.driver}" 
        url="${db.url}" 
        userid="${db.username}" 
        password="${db.password}">

        <dbconfig>
            <property name="escapePattern" value="[?]" /> 
            <property name="datatypeFactory" value="org.dbunit.ext.mssql.MsSqlDataTypeFactory" /> 
        </dbconfig>
        <msoperation type=”MSSQL_CLEAN_INSERT” format="xml" src="full_export.xml" />
    </msdbunit>

</target>

Where did I get my information?

The internet combined with trial and error. Here are the sites I used for information:

Sunday, July 6, 2008

In Response to the Easiest Java XML Binding. What About XStream?

Originally I asked the following question in this posting:

I have an XML document and I want to use that document to populate a corresponding set of Java objects. This is a commonly encountered scenario when working with Java, so what is the easiest method for Java XML Binding that requires the least amount of code?

What do I want? The 5 requirements of Java XML laziness.

I then went on to explain past methods of XML binding in Java, and what it was that I was exactly looking for. To summarize that endeavor, I was unable to locate something that fit my exact needs:

  1. I don't want to have to manually iterate through XML documents and manually map values.
  2. I don't want to have to use XSD files.
  3. I don't want to have to use libraries for binding compilers to use external binding definitions to map to Java classes.
  4. I don't want to have to modify existing XML documents so that I can use them as beans.
  5. I want to be able to have class and class field names that are different then their corresponding XML node names.

What is available?

I was aware that something had to exist that did this; I just couldn't find it. I searched the usual places such as sourcefoge.net, google, java.sun.com and so on, but everywhere I went I was bombarded with results that didn't fit my exact needs. After posting regarding this conundrum and my solution to it, which was to contribute to the problem of too many Java XML bindings by creating my own, several recommendations were made to me. The following is a list of Java XML bindings that have used and that were recommended to me:

  • JAXB - Requires Java Web Services Developer Pack v1.1 or later and the use of XSD's to bind the corresponding XML to Java interfaces (http://java.sun.com/developer/technicalArticles/WebServices/jaxb/).
  • JiBX - Open source project that uses binding definition documents to describe how XML is mapped to Java objects at binding runtime, where enhanced class files generated by the binding compiler build objects from an XML input document and can be outputted as XML documents (http://jibx.sourceforge.net/).
  • SAX/DOM/JAXP/Xerces-J - These are all types of Java XML parsers that require the manual handling of XML documents, meaning that you have to [in general] manually map the values from an XML document to the corresponding Java object (http://www.cafeconleche.org/books/xmljava/chapters/ch05.html).
  • Spring Beans - Uses the Inversion of Control container from the Spring Framework in order to bind values from special "bean" xml documents to Java classes. This is not useful for reading a specific XML document, but it is the binding that is of interest ; the automatic mapping of XML value to Java object field. (http://jvalentino.blogspot.com/2007/10/introduction-to-spring-framework.html).
  • EJB Beans - Enterprise Java Beans are a subject unto themselves, far to broad for discussion here. What is important though as with the inversion of control container from the Spring Framework, is the ability to bind values from XML documents to Java object fields (http://java.sun.com/javaee/5/docs/tutorial/doc/bnblr.html).
  • Castor - Uses marshalling and unmarshalling to go from XML to POJO and POJO to XML. The documentation is unfinished, but from what I can gather in order to go from XML to POJO and POJO to XML the POJO needs to implement serializable, and if the XML node names do not match class field names a binding file has to be used (http://www.castor.org/xml-framework.html).
  • Codehaus XFire - Another method for going from XML to POJO and POJO to XML, and requires the use of XSD files to do so (http://xfire.codehaus.org/Aegis+Binding).
  • XStream - Another method for going from XML to POJO and POJO to XML, but instead of having to use XSD files and other forms of external mappings it allows the use of annotations for aliasing (http://xstream.codehaus.org/).

What about XStream?

As it turns out XStream was exactly was I was looking for; it meets my 5 requirements of Java XML laziness. So how does it work?

Consider an RSS 2.0 document and what it represents:

<?xml version="1.0"?>
<rss version="2.0">
  <channel>
    <title>Lift Off News</title>
    <link>http://liftoff.msfc.nasa.gov/</link>
    <description>Liftoff to Space Exploration.</description>
    <language>en-us</language>
    <pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
    <lastBuildDate>Tue, 10 Jun 2003 09:41:01 GMT</lastBuildDate>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>Weblog Editor 2.0</generator>
    <managingEditor>editor@example.com</managingEditor>
    <webMaster>webmaster@example.com</webMaster>
    <ttl>5</ttl>
 
    <item>
      <title>Star City</title>
      <link>http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp</link>
      <description>How do Americans get ready to work with Russians aboard the
        International Space Station? They take a crash course in culture, language
        and protocol at Russia's Star City.</description>
      <pubDate>Tue, 03 Jun 2003 09:39:21 GMT</pubDate>
      <guid>http://liftoff.msfc.nasa.gov/2003/06/03.html#item573</guid>
    </item>
 
    <item>
      <title>Space Exploration</title>
      <link>http://liftoff.msfc.nasa.gov/</link>
      <description>Sky watchers in Europe, Asia, and parts of Alaska and Canada
        will experience a partial eclipse of the Sun on Saturday, May 31st.</description>
      <pubDate>Fri, 30 May 2003 11:06:42 GMT</pubDate>
      <guid>http://liftoff.msfc.nasa.gov/2003/05/30.html#item572</guid>
    </item>
 
    <item>
      <title>The Engine That Does More</title>
      <link>http://liftoff.msfc.nasa.gov/news/2003/news-VASIMR.asp</link>
      <description>Before man travels to Mars, NASA hopes to design new engines
        that will let us fly through the Solar System more quickly.  The proposed
        VASIMR engine would do that.</description>
      <pubDate>Tue, 27 May 2003 08:37:32 GMT</pubDate>
      <guid>http://liftoff.msfc.nasa.gov/2003/05/27.html#item571</guid>
    </item>
 
    <item>
      <title>Astronauts' Dirty Laundry</title>
      <link>http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp</link>
      <description>Compared to earlier spacecraft, the International Space
        Station has many luxuries, but laundry facilities are not one of them.
        Instead, astronauts have other options.</description>
      <pubDate>Tue, 20 May 2003 08:56:02 GMT</pubDate>
      <guid>http://liftoff.msfc.nasa.gov/2003/05/20.html#item570</guid>
    </item>
  </channel>
</rss>

Document from http://en.wikipedia.org/wiki/RSS_(file_format)

An "rss" node has a "channel" node, a "channel" node has "item" nodes, and every node has their own properties and attributes. These relationships can then be represented in terms of objects using a class diagram (ignoring methods for getters and setters):

From this class diagram it is then possible to generate the corresponding Java classes, assuming that the class fields have the same names as their corresponding XML nodes. This can't always work though since class fields can be represented in XML as nodes or attributes of nodes, so it would be necessary to designate this in Java. You may also not want to have the Java class field names the same as the XML node names, so there would need to be a way to designate this as well. This is why there will always need to be some form of mapping, which is why XStream provides annotations and also methods for their service to specify aliasing at runtime.

Bad use of annotations?

With my previous posting where I used annotations in the same manner as XStream, there was discussion regarding whether this use of annotations was necessary. The answer is absolutely yes for two reasons:

  1. For when XML node names are not the same as class and class field names.
  2. In order to translate the representation of a class field as an XML node or XML node attribute.

Consider the following class and the following XML document:

public class Example {
 public String myValue;
 public String version;
}

<example ver="1.0">
 <my_value>value</my_value>
</example>

How would one map the XML to the class when the class name and the field names are different then the XML node names? While some XML documents may be able to have their class field names interfered based on similarities, types, and positions this would not work for all cases. This is one reason why annotations or other mechanisms are need to map between class fields and XML nodes.

Also consider when converting the class to XML, how would one determine whether a class field should be represented as an XML node or a node attribute? The following XML documents all could be used to represent the data in the Example class:

<example>
 <ver>1.0</ver>
 <my_value>value</my_value>
</example>

<example ver="1.0" my_value="value" />

<example my_value="value">
 <ver>1.0</ver>
</example>

How does XStream do what you need?

XStream allows the use of annotations or object aliasing methods to specify mappings and attributes. For example here is my previous RSS 2.0 POJP example marked up for XStream using annotations:

@XStreamAlias("rss")
public class Rss2 {
 @XStreamImplicit
 private List<Channel> channel;
 @XStreamAsAttribute
 private String version;
 
 //getters and setters go here...
}

@XStreamAlias("channel")
public class Channel {
 private String title;
 private String link;
 private String description;
 private String language;
 private String pubDate;
 private String lastBuildDate;
 private String docs;
 private String generator;
 private String managingEditor;
 private String webMaster;
 private int ttl;
 @XStreamImplicit
 private List<Item> item;
 
 //getters and setters go here...
}

@XStreamAlias("item")
public class Item {
 private String title;
 private String link;
 private String description;
 private String pubDate;
 private String guid;
 
 //getters and setters go here...
}

The following code can be used to populate an RSS object using XML:

XStream xstream = new XStream(new DomDriver());
xstream.processAnnotations(Rss2.class);

//Create an RSS object and populate it using an XML file
Rss2 rssA = new Rss2();
xstream.fromXML(new FileReader(new File("test/Rss2Test.xml")), rssA);

The following code can then be used to convert a POJO to XML:

String xml = xstream.toXML(rssA);

Why did you reinvent the wheel with your RE: JAXB project?

As I have said before I created a factory for binding XML to POJOs and back because I could not find anything at the time that suited my exact needs. To me it is really not that big of a deal, because it only took a single afternoon and only consisted of several hundred lines of code.

 

Monday, June 30, 2008

The Easiest Java XML Binding

I have an XML document and I want to use that document to populate a corresponding set of Java objects. This is a commonly encountered scenario when working with Java, so what is the easiest method for Java XML Binding that requires the least amount of code? You can't answer that question without first defining what "easy" is in relation to the bindings between Java and XML. To me "easy" is synonymous with "simple," so I am equating easiness to complexity. Complexity is of interest because things that are complex take longer to implement and are generally harder to maintain, based on my experiences with software in a variety of languages and platforms. Less code written means less code to maintain, and less code to write means less time is required to write that code.

So what are the methods for binding Java and XML?

  • JAXB - Requires Java Web Services Developer Pack v1.1 or later and the use of XSD's to bind the corresponding XML to Java interfaces (http://java.sun.com/developer/technicalArticles/WebServices/jaxb/).
  • JiBX - Open source project that uses binding definition documents to describe how XML is mapped to Java objects at binding runtime, where enhanced class files generated by the binding compiler build objects from an XML input document and can be outputted as XML documents (http://jibx.sourceforge.net/).
  • SAX/DOM/JAXP/Xerces-J - These are all types of Java XML parsers that require the manual handling of XML documents, meaning that you have to [in general] manually map the values from an XML document to the corresponding Java object (http://www.cafeconleche.org/books/xmljava/chapters/ch05.html).
  • Spring Beans - Uses the Inversion of Control container from the Spring Framework in order to bind values from special "bean" xml documents to Java classes. This is not useful for reading a specific XML document, but it is the binding that is of interest ; the automatic mapping of XML value to Java object field. (http://jvalentino.blogspot.com/2007/10/introduction-to-spring-framework.html).
  • EJB Beans - Enterprise Java Beans are a subject unto themselves, far to broad for discussion here. What is important though as with the inversion of control container from the Spring Framework, is the ability to bind values from XML documents to Java object fields (http://java.sun.com/javaee/5/docs/tutorial/doc/bnblr.html).

I have used all of these methods in depth before, but this isn't exactly what I want though:

  • I don't want to have to manually iterate through XML documents and manually map values.
  • I don't want to have to use XSD files.
  • I don't want to have to use libraries for binding compilers to use binding definitions to map to Java classes.
  • I don't want to have to modify existing XML documents so that I can use them as beans.

My usage of Adobe Flex (http://jvalentino.blogspot.com/2008/02/flex-tutorial-part-ii-language-concepts.html) over the last couple of years has spoiled my expectations for language and XML interaction; I want more with less.

I know exactly what I want: I want to have a series of Java objects that are representations of nodes within an XML document, and pass the XML document to the Java object representing the root XML node and have all of the corresponding Java objects populate using that XML. I then want to be able to go from Java back to XML, and I don't want to have to do anything other then say "XML, go to Java" and then "Java, go to XML."

Why should there need to be complex mappings and external definitions when Java and XML are being used to representing the same thing?

Consider an RSS 2.0 document and what it represents:

<?xml version="1.0"?>
<rss version="2.0">
  <channel>
    <title>Lift Off News</title>
    <link>http://liftoff.msfc.nasa.gov/</link>
    <description>Liftoff to Space Exploration.</description>
    <language>en-us</language>
    <pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
    <lastBuildDate>Tue, 10 Jun 2003 09:41:01 GMT</lastBuildDate>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>Weblog Editor 2.0</generator>
    <managingEditor>editor@example.com</managingEditor>
    <webMaster>webmaster@example.com</webMaster>
    <ttl>5</ttl>
 
    <item>
      <title>Star City</title>
      <link>http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp</link>
      <description>How do Americans get ready to work with Russians aboard the
        International Space Station? They take a crash course in culture, language
        and protocol at Russia's Star City.</description>
      <pubDate>Tue, 03 Jun 2003 09:39:21 GMT</pubDate>
      <guid>http://liftoff.msfc.nasa.gov/2003/06/03.html#item573</guid>
    </item>
 
    <item>
      <title>Space Exploration</title>
      <link>http://liftoff.msfc.nasa.gov/</link>
      <description>Sky watchers in Europe, Asia, and parts of Alaska and Canada
        will experience a partial eclipse of the Sun on Saturday, May 31st.</description>
      <pubDate>Fri, 30 May 2003 11:06:42 GMT</pubDate>
      <guid>http://liftoff.msfc.nasa.gov/2003/05/30.html#item572</guid>
    </item>
 
    <item>
      <title>The Engine That Does More</title>
      <link>http://liftoff.msfc.nasa.gov/news/2003/news-VASIMR.asp</link>
      <description>Before man travels to Mars, NASA hopes to design new engines
        that will let us fly through the Solar System more quickly.  The proposed
        VASIMR engine would do that.</description>
      <pubDate>Tue, 27 May 2003 08:37:32 GMT</pubDate>
      <guid>http://liftoff.msfc.nasa.gov/2003/05/27.html#item571</guid>
    </item>
 
    <item>
      <title>Astronauts' Dirty Laundry</title>
      <link>http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp</link>
      <description>Compared to earlier spacecraft, the International Space
        Station has many luxuries, but laundry facilities are not one of them.
        Instead, astronauts have other options.</description>
      <pubDate>Tue, 20 May 2003 08:56:02 GMT</pubDate>
      <guid>http://liftoff.msfc.nasa.gov/2003/05/20.html#item570</guid>
    </item>
  </channel>
</rss>

Document from http://en.wikipedia.org/wiki/RSS_(file_format)

An "rss" node has a "channel" node, a "channel" node has "item" nodes, and every node has their own properties and attributes. These relationships can then be represented in terms of objects using a class diagram (ignoring methods for getters and setters):

From this class diagram it is then possible to generate the corresponding Java classes, assuming that the class fields have the same names as their corresponding XML nodes. This can't always work though since class fields can be represented in XML as properties or attributes, so it would be necessary to designate this in Java. You may also not want to have the Java class field names the same as the XML node names, so there would need to be a way to designate this as well. One easy way to designate specific things about classes, fields, and methods is to use annotations, so consider the following 3 annotations:

  1. @ClassXmlNodeName - used on a Java class to specify its corresponding node name in an XML document. This is required in order to designate a class as being bound to an XML document.
  2. @XmlAttributeName - used on a Java field to specify its corresponding attribute name in an XML document. This is required in order to specify that a particular class field should be represented in XML as an attribute instead of a property.
  3. @XmlNodeName - used on a Java field to specify its corresponding node name in an XML document. This is only required when a Java class field name is different than its corresponding XML node name.
@ClassXmlNodeName("rss")
public class Rss {
 private List<Channel> channels;
 @XmlAttributeName("version")
 private String version;
 
 //getters and settings go here...
}

@ClassXmlNodeName("channel")
public class Channel {
 private String title;
 private String link;
 private String description;
 private String language;
 private String pubDate;
 private String lastBuildDate;
 private String docs;
 private String generator;
 private String managingEditor;
 private String webMaster;
 private int ttl;
 private List<Item> items;

 //getters and settings go here...
}

@ClassXmlNodeName("item")
public class Item {
 private String title;
 private String link;
 private String description;
     private String pubDate;
     private String guid;
    
     //getters and settings go here... 
}

With the exception of the annotations, these Java classes are just like any other data transfer objects (DTOs) that would be used to represent a RSS XML document. These annotations act as the mappings between Java and XML when names and types do not provide enough information. With this information, the class representing the XML document root node, and the XML document it is possible to recursively map XML nodes to Java class fields and Java class fields back to XML nodes using Reflection. So using Reflection I wrote a library that can take any Plain Old Java Object (POJO) and an XML document, and use that XML document to populate that object and all of its child objects using one line of code. The same can then be done to convert a Java object back to its XML representation using a single line of code. All of this assuming that the Java classes correspond to the XML document.

Using this "XML Binder" library the following code takes the the given instance of an Rss object and populates it and its channels and items using the given XML document:

Rss rss = new Rss();
XmlBinderFactory.newInstance().bind(rss, new File("Rss2Test.xml"));

The following code can then be used to take that same Rss object once changes have been made to it and converts it back to XML:

String xml = XmlBinderFactory.newInstance().toXML(rss);

I assert that this can work with most XML documents that have appropriate corresponding Java classes, but I have only tested it out so far with RSS 1.0, RSS 2.0, and a general test XML document in no particular format. The question now is what do I do with this library; anyone interested?

Since there seems to be some interest I have started a sourceforge.net project called "Really Easy Java XML Binding" or "RE:JAXB" since this is sort of a reply to JAXB. Here is the link

I have included POJOs for RSS 1.0 and RSS 2.0 along with unit tests to verify that the mappings from XML and to XML are working correctly. What I am looking for are ways to improve the efficiency of the to and from binding code, as well as POJOs for other common XML formats.

Sunday, October 21, 2007

Introduction to the Spring Framework

What is the Spring Framework?

According to SpringFramework.org:

Spring provides a light-weight solution for building enterprise-ready applications, while still supporting the possibility of using declarative transaction management, remote access to your logic using RMI or web services, and various options for persisting your data to a database [1].

More specifically Spring is a way of using regular Java Beans as Enterprise JavaBeans without involving the complexity of J2EE. JavaBeans encapsulate many objects into a single object (the bean), so that the bean can be passed around rather than the individual objects [2]. The Enterprise JavaBean (EJB) is a managed, server-side component architecture for modular construction of enterprise applications [3], which requires a J2EE container/application server. Java 2 Enterprise Edition (J2EE) is a combination of libraries and useful technologies for the Java programming language, that are used to develop and deploy enterprise applications using modular components using a Java application server.

Spring is more than just JavaBeans though, it is a framework that contains other frameworks, which are designed to independently work well with other frameworks. These frameworks or modules are typically divided into the following:

  • Inversion of Control Container (Core Container)
  • Context Module
  • Aspect Oriented Programming (AOP) Module
  • Data Access Object (DAO) Module
  • Object-Relational Mapping (ORM) Module
  • Web Module
  • Model-View-Controller (MVC) Framework

For the purposes of introduction though, it is best to start with the basics of the Inversion Control Container. Central in the Spring Framework is its Inversion of Control Container that provides a consistent means of configuring and managing Java objects. This container is also known as BeanFactory, ApplicationContext or Core container [4]. Inversion of Control is a concept in which the control flow is inverted compared to the traditional interaction model expressed in imperative style by a series of procedure calls. Thus, instead of the programmer specifying, by the means of function calls, a series of events to happen during the lifetime of a programme, they would rather register desired responses to particular happenings, and then let some external entities take over the control over the precise order and set of events to happen [5].

Where do I get the Spring Framework and how do I install it?

The framework is a series of JAR files, which can be downloaded from http://www.springframework.org/download for fee. To install it all you really have to do is extract the downloaded compressed file to some location on your computer, and then reference the appropriate JARs in a Java project. If you download the full distribution you will get the following directories, which are specified "RELEASE INFO" section of the "readme.txt" included with the distribution:

  • dist -  contains the Spring distribution jar files, as well as a zip of all Java source files
  • docs -  contains the Spring reference documentation in PDF and HTML format, as well as the complete API javadocs
  • lib -  contains all third-party libraries needed for building the framework and/or running the samples
  • src -  contains the general Java source files for the framework
  • mock -  contains the general Java source files for Spring's mock and test classes
  • test -  contains the general Java source files for Spring's test suite
  • tiger/src - contains the JDK-1.5-specific Java source files for the framework
  • tiger/test -  contains the JDK-1.5-specific Java source files for Spring's test suite
  • aspectj/src -  contains the AspectJ-specific source files for the framework
  • aspectj/test - contains the AspectJ-specific source files for Spring's test suite
  • samples - contains various demo applications and showcases

How do I use the Spring Framework?

Considering that there are many sub-frameworks in Spring, it is easiest to start with an example that demonstrates the core basics of the Inversion of Control Container. There are many available Spring Tutorials that can be found in books and on the internet. So far the best example that I have been able to find is the Inversion of Control example from DevelopersBook.com, in which you don't directly connect your components and services together in code but describe which services are needed by which components in a configuration file [6]. I have taken the basic example and modified it to include comments and a few other properties, with the intention for later use in an Aspect Oriented Programming example. Consider a simple application framework that provides JavaBean that is used to greet as specified person using a "Greeting Service," and say good-bye to that person using a "Leaving Service." This requires the following classes, interfaces, and XML configuration:

  • HelloBean.java - This is the bean that is used to encapsulate the "Greeting Service" and "Leaving Service."
  • HelloBean.xml - This is the Spring XML configuration file that is used to specify the instance and relationships of the involved object.
  • GreetingService.java (implementing GreetingInterface.java) - This is the service that is used to generate the greeting message.
  • LeavingService.java (implementing LeavingInterface.java) - This is the service that is used to generate the leaving message.
  • HelloClient.java - This is the main class that uses the Inversion of Control Container with the Spring XML configuration file to get the specified instance of the HelloBean.

The following is the project directory structure, which shows the location of the Java source code files, the related class files, and the Spring XML configuration file:

  • bin
    • springapp
      • service
        • interfaces
          • GreetingInterface.class
          • LeavingInterface.class
        • GreetingService.class
        • LeavingService.class
      • bean
        • HelloBean.class
      • HelloClient.class
    • HelloBean.xml
  • src
    • springapp
      • service
        • interfaces
          • GreetingInterface.java
          • LeavingInterface.java
        • GreetingService.java
        • LeavingService.java
      • bean
        • HelloBean.java
      • HelloClient.java

Architecturally the following class diagram shows how all of the objects relate to one another within the "springapp" package:

The "HelloClient" uses the Spring XML configuration file "HelloBean.xml" with  the Inversion of Control Container to create the specified instance of the "HelloBean." The "HelloBean" encapsulates the objects and functionality of the "GreetingService" and "LeavingService."

For this particular application, the following libraries from the Spring Framework are required:

  • <spring-install>\lib\jakarta-commons\*.jar
  • <spring-install>\lib\log4j\lg4j-1.2.14.jar
  • <spring-install>\dist\spring.jar

GreetingService.java

This is an object that is instantiated by the "HelloBean" in order to generate the greeting message. The actual instantiation though is specified in the "HelloBean.xml" file, which is handled by the Inversion of Control Container or BeanFactory. This class only has one property, which is the variable "greetingMessage." In order for the BeanFactory to be able to allow the setting of the value of this variable the class must provide a "setter" method. In the case of the "GreetingService," it has a "setGreetingMessage" method that can used by the BeanFactory to set the value of the "greetingMessage" variable.

package springapp.service;

import springapp.service.interfaces.GreetingInterface;

/**
 * <p>Title: Greeting Service</p>
 * 
 * <p>Description: This is the service that is used to generate
 * the greeting message.</p>
 * <p>Taken and modified from Code Listing 1:
 * http://www.developersbook.com/spring/spring-tutorials/</p>
 *
 * @author John Valentino II
 */
public class GreetingService implements GreetingInterface {

 /** This is greeting message that is displayed.*/
 private String greetingMessage;

 /**
  * Returns the "Hello" message for the person of the given name.
  * @param name
  * @return String
  */
 public String sayHello(String name) {
  return greetingMessage + " " + name + ".";
 }

 /**
  * Sets the greeting message
  * @param message
  */
 public void setGreetingMessage(String message) {
  this.greetingMessage = message;
 }

}

GreetingInterface.java

The methods required to be implemented by the "GreetingService" that are public and are not "getter" or "setter" methods are specified in this interface. This is because it is considered good practice to separately specify those methods in an implemented interface.

package springapp.service.interfaces;

/**
 * <p>Title: Greeting Interface</p>
 * 
 * <p>Description: Representing the methods of the Greeting Service.</p>
 * <p>Taken and modified from Code Listing 1:
 * http://www.developersbook.com/spring/spring-tutorials/</p>
 *
 * @author John Valentino II
 */
public interface GreetingInterface {

 /**
  * Returns the "Hello" message for the person of the given name.
  * @param name
  * @return String
  */
 public String sayHello(String name);
}

LeavingService.java

This is an object that is instantiated by the "HelloBean" in order to generate the leaving message. The actual instantiation though is specified in the "HelloBean.xml" file, which is handled by the Inversion of Control Container or BeanFactory. This class only has one property, which is the variable "leavingMessage." In order for the BeanFactory to be able to allow the setting of the value of this variable the class must provide a "setter" method. In the case of the "LeavingService," it has a "setLeavingMessage" method that can used by the BeanFactory to set the value of the "leavingMessage" variable.

package springapp.service;

import springapp.service.interfaces.LeavingInterface;

/**
 * <p>Title: Leaving Service</p>
 * 
 * <p>Description: This is the service that is used to generate
 * the leaving message.</p>
 * <p>Taken and modified from Code Listing 2:
 * http://www.developersbook.com/spring/spring-tutorials/</p>
 *
 * @author John Valentino II
 */
public class LeavingService implements LeavingInterface {

 /** The good-bye message to be displayed. */
 private String leavingMessage;

 /**
  * Returns the "Good-Bye" message for the person of the given name
  * @param name
  * @return String
  */
 public String sayGoodBye(String name) {
  return leavingMessage + " " + name + ".";
 }

 /**
  * Sets the good-bye message
  * @param message
  */
 public void setLeavingMessage(String message) {
  this.leavingMessage = message;
 }

}

LeavingInterface.java

The methods required to be implemented by the "LeavingService" that are public and are not "getter" or "setter" methods are specified in this interface.

package springapp.service.interfaces;

/**
 * <p>Title: Leaving Interface</p>
 * 
 * <p>Required methods for the Leaving Service.</p>
 * <p>Taken and modified from Code Listing 2:
 * http://www.developersbook.com/spring/spring-tutorials/</p>
 * 
 * @author John Valentino II
 *
 */
public interface LeavingInterface {

 /**
  * Returns the "Good-Bye" message for the person of the given name
  * @param name
  * @return String
  */
 public String sayGoodBye(String name);

}

HelloBean.java

This is the JavaBean that is used to encapsulate the services of this example application. Since this class has three properties, it specifies three "setter" methods to allow the setting of instances or values by the BeanFactory or other parent classes.

package springapp.bean;

import springapp.service.GreetingService;
import springapp.service.LeavingService;

/**
 * <p>Title: Hello Bean</p>
 * 
 * <p>Description: Represents the Hello Bean.</p>
 * <p>Taken and modified from Code Listing 4:
 * http://www.developersbook.com/spring/spring-tutorials/</p>
 *
 * @author John Valentino II
 */
public class HelloBean {

 /** Represents the service used to generate a greeting */
 private GreetingService greetingService;
 /** Represents the service used to generate a good-bye message */
 private LeavingService leavingService;
 /** Represents the name of the person to meet or leave */
 private String personName;

 /**
  * Sets the name of the person
  * @param name
  */
 public void setPersonName(String name) {
  this.personName = name;
 }

 /**
  * Sets the instance of the greeting service
  * @param service
  */
 public void setGreetingService(GreetingService service) {
  this.greetingService = service;
 }

 /**
  * Set the instance of the leaving service
  * @param service
  */
 public void setLeavingService(LeavingService service) {
  this.leavingService = service;
 }

 /**
  * Generates the greeting message
  * @return
  */
 public String greet() {
  return greetingService.sayHello(personName);
 }

 /**
  * Generates the leaving message
  * @return
  */
 public String leave() {
  return leavingService.sayGoodBye(personName);
 }

}

HelloClient.java

This is the main class of the application that uses the "HelloBean.xml" with the Inversion of Control Container to create the instance of the "HelloBean" and call is available methods.

package springapp;

import springapp.bean.HelloBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * <p>Title: Hello Client</p>
 * 
 * <p>Description: Represents a program that uses the HelloBean.</p>
 * <p>Taken and modified from Code Listing 5:
 * http://www.developersbook.com/spring/spring-tutorials/</p>
 *
 * @author John Valentino II
 */
public class HelloClient {

 /**
  * @param args
  */
 public static void main(String[] args) {

  //Get the instance of the HelloBean as specified in HelloBean.xml
  ClassPathXmlApplicationContext appContext = 
   new ClassPathXmlApplicationContext(new String[] { "HelloBean.xml" });
  HelloBean helloBean = (HelloBean) appContext.getBean("helloBean");

  System.out.println(helloBean.greet());
  System.out.println(helloBean.leave());

 }

}

HelloBean.xml

This is the Spring XML configuration file that is used to specify the instances and properties of the bean classes within the application.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
 <!-- Creates an instance of the "HelloBean" class -->
 <bean id="helloBean" class="springapp.bean.HelloBean">
  <!-- Sets the value of "personName" by
       calling "setPersonName" -->
  <property name="personName">
   <value>Sarah</value>
  </property>
  <!-- Sets the instance of "greetingService" by
       calling "setGreetingService" -->
  <property name="greetingService"> 
   <ref bean="greetingService" />
  </property>
  <!-- Sets the instance of "leavingService" by
       calling "setLeavingService" -->
  <property name="leavingService">
   <ref bean="leavingService" /> 
  </property> 
 </bean>
 <!-- Creates an instance of the "GreetingService" -->
 <bean id="greetingService" class="springapp.service.GreetingService">
  <!-- Sets the value of "greetingMessage" by 
         calling "setGreetingMessage" -->
  <property name="greetingMessage">
   <value>Hello</value>
  </property>
 </bean>
 <!-- Creates an instance of the "LeavingService" -->
 <bean id="leavingService" class="springapp.service.LeavingService">
  <!-- Sets the value of "leavingMessage" by
       calling "setLeavingMessage" -->
  <property name="leavingMessage">
   <value>Good-bye</value>
  </property>
 </bean>
</beans>

Output

The program runs and generates the following output:

Hello Sarah.
Good-bye Sarah.

This output is to be expected as the Spring XML configuration file specifies that the "HelloBean" instance has the specified "personName," an instance of the "GreetingService," and an instance of the "LeavingService." Both service instances are also given specified "greetingMessage" and "leavingMessage" values.

Links

References

  1. SpringFramework.org, Documentation Preface, http://static.springframework.org/spring/docs/2.0.x/reference/preface.html
  2. Wikipedia, JavaBean, http://en.wikipedia.org/wiki/JavaBeans
  3. Wikipedia, Enterprise JavaBean, http://en.wikipedia.org/wiki/Ejb
  4. Wikipedia, Spring Framework, http://en.wikipedia.org/wiki/Spring_framework
  5. Wikipedia, Inversion of Control, http://en.wikipedia.org/wiki/Inversion_of_Control
  6. DevlopersBook.com, Spring Framework Tutorial, http://www.developersbook.com/spring/spring-tutorials/spring-tutorials.php

Contributors