Tuesday 24 May 2011

How to bind session beans to JNDI in JBoss

Introduction
To override the default way session beans bind to JNDI in JBoss you can use @LocalBinding or @RemoteBinding annotations. Please note that these annotations are not standard on the EJB3 specification, they are JBoss specific (they are located in org.jboss.ejb3.annotation). This post is based on JBoss Documentation - Binding your beans in JNDI.

1. Maven configuration
The following dependencies need to be added to your pom to get the LocalBinding and RemoteBinding working:

  
        org.jboss.ejb3  
        jboss-ejb3-ext-api  
        1.1.1  
        provided  
          
            
            org.jboss.javaee  
            jboss-ejb-api  
            
            
            org.jboss.metadata  
            jboss-metadata  
            
          
         

2. Local JNDI Binding
The @LocalBinding annotation allows you to change the JNDI name for an EJB local interface. The following code snippet shows the usage:

package com.example.local;   
import org.jboss.ejb3.annotation.LocalBinding;

@Local
@LocalBinding(jndiBinding = "ExampleService/local")
public interface ExampleService { 
   
}

The previous EJB will bind with to JNDI as ExampleService/local. The server log shows the EJB has been deployed as described:

2011-05-08 08:20:20,306 117281 INFO  [org.jboss.ejb3.session.SessionSpecContainer] (RMI TCP Connection(10)-127.0.0.1:) Starting jboss.j2ee:ear=example.ear,jar=example-ejbs-1.0.0.jar,name=ExampleServiceImpl,service=EJB3
2011-05-08 08:20:20,306 117281 INFO  [org.jboss.ejb3.EJBContainer] (RMI TCP Connection(10)-127.0.0.1:) STARTED EJB: com.example.ejbs.ExampleServiceImpl ejbName: ExampleServiceImpl
2010-05-08 08:20:20,353 117328 INFO  [org.jboss.ejb3.proxy.impl.jndiregistrar.JndiSessionRegistrarBase] (RMI TCP Connection(10)-127.0.0.1:) Binding the following Entries in Global JNDI:

 ExampleService/local - EJB3.x Default Local Business Interface
 example/ExampleServiceImpl/local-com.example.local.ExampleService - EJB3.x Local Business Interface

3. Remote Interface JNDI Binding
The @RemoteBinding annotation allows you to change the JNDI name for an EJB remote interface. The following code snippet shows the usage:

package com.example.remote;
import org.jboss.ejb3.annotation.RemoteBinding;
  
 
@Remote
@RemoteBinding(jndiBinding="ExampleService/remote")
public interface ExampleService { 
    
}

The server log shows the following JNDI names:

2010-05-08 08:20:21,306 117281 INFO  [org.jboss.ejb3.session.SessionSpecContainer] (RMI TCP Connection(10)-127.0.0.1:) Starting jboss.j2ee:ear=example.ear,jar=example-ejbs-1.0.0.jar,name=ExampleServiceImpl,service=EJB3
2010-05-08 08:20:21,306 117281 INFO  [org.jboss.ejb3.EJBContainer] (RMI TCP Connection(10)-127.0.0.1:) STARTED EJB: com.example.ejbs.ExampleServiceImpl ejbName: ExampleServiceImpl
2010-05-08 08:20:21,353 117328 INFO  [org.jboss.ejb3.proxy.impl.jndiregistrar.JndiSessionRegistrarBase] (RMI TCP Connection(10)-127.0.0.1:) Binding the following Entries in Global JNDI:

 ExampleService/remote - EJB3.x Default Remote Business Interface
 example/ExampleServiceImpl/remote-com.example.remote.ExampleService - EJB3.x Remote Business Interface

4. More over RemoteBindings
By default JBoss EJB3 uses a socket based invoker layer on port 3878 (i.e. socket://0.0.0.0:3878). It is possible, for example, to use SSL as the transport (i.e. sslsocket://0.0.0.0:3843). You can find plenty of documentation on how to enable SSL transport on your EJBs.

You can also enable different types of communication for your beans:

package com.example.remote;   
import org.jboss.ejb3.annotation.RemoteBindings;
import org.jboss.ejb3.annotation.RemoteBinding;

@Remote
@RemoteBindings(
        {
                @RemoteBinding(jndiBinding="custom/ExampleService/remote"),
                @RemoteBinding(jndiBinding="custom/ExampleServiceImpl/remote", clientBindUrl="socket://0.0.0.0:3333"),
                @RemoteBinding(jndiBinding="ssl/ExampleServiceImpl/remote", clientBindUrl="sslsocket://0.0.0.0:3843")
        }
)
public interface ExampleService {
}

The server.log file shows the following JNDI names:

2010-05-08 08:20:20,306 117281 INFO  [org.jboss.ejb3.session.SessionSpecContainer] (RMI TCP Connection(10)-127.0.0.1:) Starting jboss.j2ee:ear=example.ear,jar=example-ejbs-1.0.0.jar,name=ExampleServiceImpl,service=EJB3
2010-05-08 08:20:20,306 117281 INFO  [org.jboss.ejb3.EJBContainer] (RMI TCP Connection(10)-127.0.0.1:) STARTED EJB: com.example.ejbs.ExampleServiceImpl ejbName: ExampleServiceImpl
2010-05-08 08:20:20,353 117328 INFO  [org.jboss.ejb3.proxy.impl.jndiregistrar.JndiSessionRegistrarBase] (RMI TCP Connection(10)-127.0.0.1:) Binding the following Entries in Global JNDI:

 custom/ExampleService/remote - EJB3.x Default Remote Business Interface
 custom/ExampleServiceImpl/remote - EJB3.x Default Remote Business Interface
 ssl/ExampleServiceImpl/remote - EJB3.x Default Remote Business Interface
 example/ExampleServiceImpl/remote-com.example.remote.ExampleService - EJB3.x Remote Business Interface

Wednesday 20 April 2011

How to configure XA transactions with Microsoft SQL Server and TIBCO EMS Server in JBoss

This article describes the processes involved in enabling XA transactions within an SQL Server and TIBCO EMS environment.

Prerequisites
1. SQL Server JDBC Driver
2. JBoss 5.1.0
3. TIBCO EMS server
4. An SQL Server 2000/2005 instance

Enabling XA transactions in SQL Server:
1. Download the SQL Server JDBC Driver from here and unzip the file.

2. Prior to running the SQL script you must copy the extended stored procedure dll SQLJDBC_XA.dll to the target SQL Server's Binn folder (typically in C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn).

3. Permissions to the distributed transaction support procedures for the Microsoft SQL Server JDBC Driver 2.0 are granted through the SQL Server role SqlJDBCXAUser. To maintain a secure default configuration, no user is granted access to this role by default.

4. Run the following SQL script in the target SQL Server:

use master
go

-- Drop any existing procedure definitions.
exec sp_dropextendedproc 'xp_sqljdbc_xa_init' 
exec sp_dropextendedproc 'xp_sqljdbc_xa_start'
exec sp_dropextendedproc 'xp_sqljdbc_xa_end'
exec sp_dropextendedproc 'xp_sqljdbc_xa_prepare'
exec sp_dropextendedproc 'xp_sqljdbc_xa_commit'
exec sp_dropextendedproc 'xp_sqljdbc_xa_rollback'
exec sp_dropextendedproc 'xp_sqljdbc_xa_forget'
exec sp_dropextendedproc 'xp_sqljdbc_xa_recover'
exec sp_dropextendedproc 'xp_sqljdbc_xa_rollback_ex'
exec sp_dropextendedproc 'xp_sqljdbc_xa_forget_ex'
exec sp_dropextendedproc 'xp_sqljdbc_xa_prepare_ex'
exec sp_dropextendedproc 'xp_sqljdbc_xa_init_ex'
go

-- Install the procedures.
exec sp_addextendedproc 'xp_sqljdbc_xa_init', 'SQLJDBC_XA.dll'
exec sp_addextendedproc 'xp_sqljdbc_xa_start', 'SQLJDBC_XA.dll'
exec sp_addextendedproc 'xp_sqljdbc_xa_end', 'SQLJDBC_XA.dll'
exec sp_addextendedproc 'xp_sqljdbc_xa_prepare', 'SQLJDBC_XA.dll'
exec sp_addextendedproc 'xp_sqljdbc_xa_commit', 'SQLJDBC_XA.dll'
exec sp_addextendedproc 'xp_sqljdbc_xa_rollback', 'SQLJDBC_XA.dll'
exec sp_addextendedproc 'xp_sqljdbc_xa_forget', 'SQLJDBC_XA.dll'
exec sp_addextendedproc 'xp_sqljdbc_xa_recover', 'SQLJDBC_XA.dll'
exec sp_addextendedproc 'xp_sqljdbc_xa_rollback_ex', 'SQLJDBC_XA.dll'
exec sp_addextendedproc 'xp_sqljdbc_xa_forget_ex', 'SQLJDBC_XA.dll'
exec sp_addextendedproc 'xp_sqljdbc_xa_prepare_ex', 'SQLJDBC_XA.dll'
exec sp_addextendedproc 'xp_sqljdbc_xa_init_ex', 'SQLJDBC_XA.dll'
go

-- Create the [SqlJDBCXAUser] role in master database.
-- The SQL administrator can later add users to this role to allow users to participate 
-- in Microsoft SQL Server JDBC Driver 2.0 distributed transactions.
sp_addrole [SqlJDBCXAUser]
go

-- Grant privileges to [SqlJDBCXAUser] role to the extended stored procedures.
grant execute on xp_sqljdbc_xa_init to [SqlJDBCXAUser]
grant execute on xp_sqljdbc_xa_start to [SqlJDBCXAUser]
grant execute on xp_sqljdbc_xa_end to [SqlJDBCXAUser]
grant execute on xp_sqljdbc_xa_prepare to [SqlJDBCXAUser]
grant execute on xp_sqljdbc_xa_commit to [SqlJDBCXAUser]
grant execute on xp_sqljdbc_xa_rollback to [SqlJDBCXAUser]
grant execute on xp_sqljdbc_xa_recover to [SqlJDBCXAUser]
grant execute on xp_sqljdbc_xa_forget to [SqlJDBCXAUser]
grant execute on xp_sqljdbc_xa_rollback_ex to [SqlJDBCXAUser]
grant execute on xp_sqljdbc_xa_forget_ex to [SqlJDBCXAUser]
grant execute on xp_sqljdbc_xa_prepare_ex to [SqlJDBCXAUser]
grant execute on xp_sqljdbc_xa_init_ex to [SqlJDBCXAUser]
go

-- Add users to the [SqlJDBCXAUser] role as needed.

-- Example for adding a SQL authentication user to the SqlJDBCXAUser role.
-- exec sp_addrolemember [SqlJDBCXAUser], 'MySQLUser'

-- Example for adding a windows domain user to the SqlJDBCXAUser role.
-- exec sp_addrolemember [SqlJDBCXAUser], 'MyDomain\MyWindowsUser'

print ''
print 'SQLJDBC XA DLL installation script complete.'
print 'Check for any error messages generated above.'

5. From Control Panel, open Administrative Tools, and then open Component Services. You can also click the Start button, click Run, type dcomcnfg in the Open box, and then press OK to open Component Services.

6. Expand Component Services, Computers and right-click My Computer, and then select Properties.

7. Click the MSDTC tab, and then click Security Configuration.

8. Select the Enable XA Transactions check box, and then click OK. This will cause a MS DTC service restart.

9. Click OK again to close the Properties dialog box, and then close Component Services.

10. Stop and then restart SQL Server to ensure that it syncs up with the MS DTC changes.

Configuring an XA Datasource
This is a sample XA Datasource to connect to SQL Server 2005 and SQL Server 2000. This -ds.xml file must be place under the deploy folder of your server.

serviceName-ds.xml





    
        nameOfDataSource
        
        com.microsoft.sqlserver.jdbc.SQLServerXADataSource
        server
        database
        cursor
        username
        password
        jdbc:sqlserver://server:1433;databasename=database
        false
        
        select 1
        
        select 1
        
        
            
            MS SQLSERVER2000
        

    



Configuring TIBCO EMS Provider
This is a sample JMS Provider to connect to TIBCO EMS Server. Please note that the Connection Factory uses tx-connection-factory element which tells the Transaction Manager that this connection will take part in JTA transactions. This -ds.xml file must be place under the deploy folder of your server.

ems-ds.xml





EmsQueueProvider
org.jboss.jms.jndi.JNDIProviderAdapter
XAQueueConnectionFactory
XAQueueConnectionFactory
XATopicConnectionFactory

java.naming.factory.initial=com.tibco.tibjms.naming.TibjmsInitialContextFactory
java.naming.provider.url=tcp://server:port
java.naming.security.principal=username
java.naming.security.credentials=password




EMSQueueFactory
jms-ra.rar
org.jboss.resource.adapter.jms.JmsConnectionFactory
javax.jms.Queue
java:/EmsQueueProvider
20
username
password



Also add the following factories to your EMS Server as follows:

create factory XAQueueConnectionFactory xaqueue url=tcp://server:port
create factory XATopicConnectionFactory xatopic url=tcp://server:port
create factory XAConnectionFactory xageneric url=tcp://server:port
Sending messages to destinations
The following code snippet creates an Connection, creates a transacted session and sends a message to a queue. The message will appear in the queue when the container commits the transaction.

Context ctx = new InitialContext();
        // get the connection factory fron jndi and create a connection
        ConnectionFactory connectionFactory = (ConnectionFactory)ctx.lookup("java:/EMSQueueFactory");
        Connection connection = connectionFactory.createConnection();

        // create a session
        QueueSession session = connection.createQueueSession(true,javax.jms.Session.AUTO_ACKNOWLEDGE);
        Queue queue = session.createQueue(queueName);
        QueueSender sender = session.createSender(queue);

        // create an send a message
        msg = session.createTextMessage();
        msg.setText("Hello");
        sender.send(queue, msg);

        // close the connection
        connection.close();

Sunday 3 April 2011

JBoss 5 + Apache Camel + Spring + EJB

The following is a step-by-step guide to how to make Apache Camel 2.6.0 and Spring work with JBoss 5.

Step 1. Install JBoss
Download JBoss Application Server 5.1.0.GA. Extract the contents of the zip file to a directory of your choice (i.e. c:\jboss-5.1.0.GA). We are going to use the default server for this example.

Step 2. Install Snowdrop
Download JBoss Snowdrop version 1.1.0.GA. Snowdrop is a utility package that contains JBoss-specific extensions to the Spring Framework. Extract the contents of the zip file (spring.deployer) to the deployers folder (C:\jboss-5.1.0.GA\server\default\deployers):

Step 3. Download camel-jboss package scan loader
Download camel-jboss from camel-extra. The lirary contains JBoss specific package scan classloader to be used when Camel is running inside JBoss Application Server. JBoss uses LPGL license which means that camel-jboss cannot be hosted at Apache. Alternatively you can get a copy of the library from here.

Step 4. Install camel-jboss in Maven repository
Install the camel-jboss library in your Maven repository as follows:

mvn install:install-file -DgroupId=org.apache.camel -DartifactId=camel-jboss -Dversion=2.3.0 -Dpackaging=jar -Dfile=/path/to/camel-jboss-2.3.0.jar

Step 5. Include Maven dependencies
Add the following dependencies to your ejb's pom:

            org.jboss.ejb3
            jboss-ejb3-ext-api
            1.0.0
            
                
                    org.jboss.javaee
                    jboss-ejb-api
                
                
                    org.jboss.metadata
                    jboss-metadata
                
            
        
        
            javax.ejb
            ejb-api
            3.0
            provided
        
        
            org.springframework
            spring-beans
            3.0.2.RELEASE
        
        
            org.springframework
            spring-core
            3.0.2.RELEASE
        
        
            org.jboss.snowdrop
            snowdrop-deployers
            1.1.0.GA
        

        
            org.apache.camel
            camel-core
            2.6.0
        

        
            org.apache.camel
            camel-spring
            2.6.0
        

        
            org.apache.xbean
            xbean-spring
            3.4
            
                
                    org.springframework
                    spring
                
            
        

        
            org.apache.camel
            camel-jboss
            2.3.0
        

Step 6. Add JBoss class resolver
You will have to set the class resolver on the CamelContext which can be done using Java DSL as follows:

PackageScanClassResolver jbossResolver = new JBossPackageScanClassResolver();

  CamelContext context = new DefaultCamelContext();
  context.setPackageScanClassResolver(jbossResolver);

or adding the following bean definition in the jboss-spring.xml file using Spring XML:



Step 7. Create your RouteBuilder and add it to jboss-spring.xml
By doing so you let JBoss Application Server container manage Camel’s lifecycle. First you define the route:
package org.example.camel;

import org.apache.camel.builder.RouteBuilder;

/**
 * @author Eduardo Sanchez-Ros
 */
public class ExampleRouteBuilder extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        
        from("file:C:\\temp\\source")
                .convertBodyTo(String.class)
                .process(new ExampleRouteProcessor())
                .to("file:C:\\temp\\destination");

    }
}

and then you add it to the jboss-spring.xml file:



    
    
        
    

Step 8. Create your enterprise application
To demonstrate the previous I have created an example project that will create a route from file://C:\temp\source folder to file://C:\temp\destination and display the content of the file by calling a Stateless session bean.

Step 9. Build and deploy your ear
Copy your enterprise archive into the deploy folder.

You can download the source code from here.

Sunday 27 March 2011

Serializing Generics - SerializableSortedDictionary<T>

The SerializableSortedDictionary<T> class extends SortedDictionary<T> and implements IXmlSerializable, allowing you to serialize a generic stack into XML:

/*
 * SerializableGenerics
 * Copyright (c) 2009, Eduardo Sanchez-Ros
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;

namespace SerializableGenerics
{
    /// <summary>
    /// Represents a serializable collection of key/value pairs that are sorted on the key.
    /// </summary>
    /// <typeparam name="TKey">The type of the keys on the dictionary.</typeparam>
    /// <typeparam name="TValue">The type of the values on the dictionary.</typeparam>
    public class SerializableSortedDictionary<TKey, TValue> : SortedDictionary<TKey, TValue>, IXmlSerializable
    {
        // store key and value types
        private readonly Type m_tKey = typeof(TKey);
        private readonly Type m_tValue = typeof(TValue);

        /// <summary>
        /// Returns a string that represents the current SerializableDictionary.
        /// </summary>
        /// <returns>
        /// A string that represents the current SerializableDictionary.
        /// </returns>
        public override string ToString()
        {
            return SerializableGenerics.GetTypeName(GetType());
        }

        #region IXmlSerializable Members

        /// <summary>
        /// This property is reserved, apply the System.Xml.Serialization.XmlSchemaProviderAttribute
        /// to the class instead.
        /// </summary>
        /// <returns>
        /// An System.Xml.Schema.XmlSchema that describes the XML representation of the
        /// object that is produced by the System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)
        /// method and consumed by the System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)
        /// method.
        /// </returns>
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Generates an object from its XML representation.
        /// </summary>
        /// <param name="reader">The System.Xml.XmlReader stream from which the object is deserialized.</param>
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            // Create xml serializers for key and value
            XmlSerializer keySerializer = new XmlSerializer(m_tKey);
            XmlSerializer valueSerializer = new XmlSerializer(m_tValue);

            // Get key-value pair name
            string keyValuePairName = SerializableGenerics.GetKeyValuePairName(m_tKey, m_tValue);

            // Read start element and move to content
            reader.ReadStartElement();
            reader.MoveToContent();

            // Is keyValuePairName the start element
            if (!reader.IsStartElement(keyValuePairName))
            {
                // Throw an exception
                throw new XmlException("Starting element " + keyValuePairName + " not found.");
            }

            // Loop through key-value pairs
            while (reader.IsStartElement(keyValuePairName))
            {
                // Read key-value pair and move to content
                reader.ReadStartElement(keyValuePairName);
                reader.MoveToContent();

                // Deserialize key and value
                TKey key = (TKey)keySerializer.Deserialize(reader);
                TValue value = (TValue)valueSerializer.Deserialize(reader);

                // Read end element and add key-value pair to dictionary
                reader.ReadEndElement();
                Add(key, value);
            }

            // Read end element and move to content
            reader.ReadEndElement();
            reader.MoveToContent();
        }

        /// <summary>
        /// Converts an object into its XML representation.
        /// </summary>
        /// <param name="writer">The System.Xml.XmlWriter stream to which the object is serialized.</param>
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            // Create xml serializers for key and value
            XmlSerializer keySerializer = new XmlSerializer(m_tKey);
            XmlSerializer valueSerializer = new XmlSerializer(m_tValue);

            // Get key-value pair name
            string keyValuePairName = SerializableGenerics.GetKeyValuePairName(m_tKey, m_tValue);

            Enumerator enumerator = GetEnumerator();
            while (enumerator.MoveNext())
            {
                // Get current key value pair
                KeyValuePair<TKey, TValue> keyValuePair = enumerator.Current;

                // Write start element with key-value pair name 
                writer.WriteStartElement(keyValuePairName);

                // Serialize key and value
                keySerializer.Serialize(writer, keyValuePair.Key);
                valueSerializer.Serialize(writer, keyValuePair.Value);

                // Write end element with key-value pair name
                writer.WriteEndElement();
            }
        }

        #endregion
    }
}

Serializing Generics - SerializableSortedList<T>

The SerializableSortedList<T> class extends SortedList<T> and implements IXmlSerializable, allowing you to serialize a generic sorted list into XML:

/*
 * SerializableGenerics
 * Copyright (c) 2009, Eduardo Sanchez-Ros
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;

namespace SerializableGenerics
{
    /// <summary>
    /// Represents a serializable collection of key/value pairs that are sorted by key based on
    ///     the associated System.Collections.Generic.IComparer<T> implementation.
    /// </summary>
    /// <typeparam name="TKey">The type of the keys on the dictionary.</typeparam>
    /// <typeparam name="TValue">The type of the values on the dictionary.</typeparam>
    public class SerializableSortedList<TKey, TValue> : SortedList<TKey, TValue>, IXmlSerializable
    {
        // store key and value types
        private readonly Type m_tKey = typeof(TKey);
        private readonly Type m_tValue = typeof(TValue);

        /// <summary>
        /// Returns a string that represents the current SerializableDictionary.
        /// </summary>
        /// <returns>
        /// A string that represents the current SerializableDictionary.
        /// </returns>
        public override string ToString()
        {
            return SerializableGenerics.GetTypeName(GetType());
        }

        #region IXmlSerializable Members

        /// <summary>
        /// This property is reserved, apply the System.Xml.Serialization.XmlSchemaProviderAttribute
        /// to the class instead.
        /// </summary>
        /// <returns>
        /// An System.Xml.Schema.XmlSchema that describes the XML representation of the
        /// object that is produced by the System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)
        /// method and consumed by the System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)
        /// method.
        /// </returns>
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Generates an object from its XML representation.
        /// </summary>
        /// <param name="reader">The System.Xml.XmlReader stream from which the object is deserialized.</param>
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            // Create xml serializers for key and value
            XmlSerializer keySerializer = new XmlSerializer(m_tKey);
            XmlSerializer valueSerializer = new XmlSerializer(m_tValue);

            // Get key-value pair name
            string keyValuePairName = SerializableGenerics.GetKeyValuePairName(m_tKey, m_tValue);

            // Read start element and move to content
            reader.ReadStartElement();
            reader.MoveToContent();

            // Is keyValuePairName the start element
            if (!reader.IsStartElement(keyValuePairName))
            {
                // Throw an exception
                throw new XmlException("Starting element " + keyValuePairName + " not found.");
            }

            // Loop through key-value pairs
            while (reader.IsStartElement(keyValuePairName))
            {
                // Read key-value pair and move to content
                reader.ReadStartElement(keyValuePairName);
                reader.MoveToContent();

                // Deserialize key and value
                TKey key = (TKey)keySerializer.Deserialize(reader);
                TValue value = (TValue)valueSerializer.Deserialize(reader);

                // Read end element and add key-value pair to dictionary
                reader.ReadEndElement();
                Add(key, value);
            }

            // Read end element and move to content
            reader.ReadEndElement();
            reader.MoveToContent();
        }

        /// <summary>
        /// Converts an object into its XML representation.
        /// </summary>
        /// <param name="writer">The System.Xml.XmlWriter stream to which the object is serialized.</param>
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            // Create xml serializers for key and value
            XmlSerializer keySerializer = new XmlSerializer(m_tKey);
            XmlSerializer valueSerializer = new XmlSerializer(m_tValue);

            // Get key-value pair name
            string keyValuePairName = SerializableGenerics.GetKeyValuePairName(m_tKey, m_tValue);

            IEnumerator<KeyValuePair<TKey, TValue>> enumerator = GetEnumerator();
            while (enumerator.MoveNext())
            {
                // Get current key value pair
                KeyValuePair<TKey, TValue> keyValuePair = enumerator.Current;

                // Write start element with key-value pair name 
                writer.WriteStartElement(keyValuePairName);

                // Serialize key and value
                keySerializer.Serialize(writer, keyValuePair.Key);
                valueSerializer.Serialize(writer, keyValuePair.Value);

                // Write end element with key-value pair name
                writer.WriteEndElement();
            }
        }

        #endregion
    }
}

Serializing Generics - SerializableStack<T>

The SerializableStack<T> class extends Stack<T> and implements IXmlSerializable, allowing you to serialize a generic stack into XML:

/*
 * SerializableGenerics
 * Copyright (c) 2009, Eduardo Sanchez-Ros
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;

namespace SerializableGenerics
{
    /// <summary>
    /// Represents a variable size last-in-first-out (LIFO) serializable collection of instances
    /// of the same arbitrary type.
    /// </summary>
    /// <typeparam name="T">The type of the items on the linked list.</typeparam>
    public class SerializableStack<T> : Stack<T>, IXmlSerializable
    {
        // store list type
        private readonly Type m_type = typeof(T);

        /// <summary>
        /// Returns a string that represents the current SerializableDictionary.
        /// </summary>
        /// <returns>
        /// A string that represents the current SerializableDictionary.
        /// </returns>
        public override string ToString()
        {
            return SerializableGenerics.GetTypeName(GetType());
        }

        #region IXmlSerializable Members

        /// <summary>
        /// This property is reserved, apply the System.Xml.Serialization.XmlSchemaProviderAttribute
        /// to the class instead.
        /// </summary>
        /// <returns>
        /// An System.Xml.Schema.XmlSchema that describes the XML representation of the
        /// object that is produced by the System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)
        /// method and consumed by the System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)
        /// method.
        /// </returns>
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Generates an object from its XML representation.
        /// </summary>
        /// <param name="reader">The System.Xml.XmlReader stream from which the object is deserialized.</param>
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            // Create xml serializer for type
            XmlSerializer typeSerializer = new XmlSerializer(m_type);

            // Read start element and move to content
            reader.ReadStartElement();
            reader.MoveToContent();

            // Loop through elements
            while (reader.NodeType != XmlNodeType.EndElement)
            {
                // Deserialize type
                T item = (T)typeSerializer.Deserialize(reader);

                // Add node to stack
                Push(item);
            }

            // Read end element and move to content
            reader.ReadEndElement();
            reader.MoveToContent();
        }

        /// <summary>
        /// Converts an object into its XML representation.
        /// </summary>
        /// <param name="writer">The System.Xml.XmlWriter stream to which the object is serialized.</param>
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            // Create xml serializer for type
            XmlSerializer typeSerializer = new XmlSerializer(m_type);

            IEnumerator<T> enumerator = GetEnumerator();
            while (enumerator.MoveNext())
            {
                // Serialize type
                typeSerializer.Serialize(writer, enumerator.Current);
            }
        }

        #endregion
    }
}

The previous class uses the SerializableGenerics helper class which constructs the element name based on the key-value pair node.

Serializing Generics

The following exceptions are thrown when you try to serialize a any of the types (except for List) in the System.Collections.Generic namespace:

A first chance exception of type 'System.NotSupportedException' occurred in System.Xml.dll
System.NotSupportedException: The type System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] is not supported because it implements IDictionary.

A first chance exception of type 'System.InvalidOperationException' occurred in System.Xml.dll
System.InvalidOperationException: You must implement a default accessor on System.Collections.Generic.Queue`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] because it inherits from ICollection.
at System.Xml.Serialization.TypeScope.GetDefaultIndexer(Type type, String memberInfo)
at System.Xml.Serialization.TypeScope.ImportTypeDesc(Type type, MemberInfo memberInfo, Boolean directReference)
at System.Xml.Serialization.TypeScope.GetTypeDesc(Type type, MemberInfo source, Boolean directReference, Boolean throwOnError)
at System.Xml.Serialization.ModelScope.GetTypeModel(Type type, Boolean directReference)
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type, XmlRootAttribute root, String defaultNamespace)
at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
at System.Xml.Serialization.XmlSerializer..ctor(Type type)

A way around this problem is to implement the IXmlSerializable interface.

The following classes will allow you serialize generic types to XML in .NET 2.0:


You can download the library's source code here.

The previous classes use the SerializableGenerics helper class which constructs the element name based on the key-value pair node:

/*
 * SerializableGenerics
 * Copyright (c) 2009, Eduardo Sanchez-Ros
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;

namespace SerializableGenerics
{
    public static class SerializableGenerics
    {
        private const string OF = "Of";

        /// <summary>
        /// Gets the key-value pair name
        /// </summary>
        /// <param name="tKey"></param>
        /// <param name="tValue"></param>
        /// <returns></returns>
        public static string GetKeyValuePairName(Type tKey, Type tValue)
        {
            // return key-value pair name
            return GetTypeName(tKey) + GetTypeName(tValue);
        }

        /// <summary>
        /// Returns the name of the type
        /// </summary>
        /// <param name="type">Type to generate the name from</param>
        /// <returns>The name of the type</returns>
        public static String GetTypeName(Type type)
        {
            String typeName;

            // Is type generic
            if (type.IsGenericType)
            {
                // Get type name - Generics 
                typeName = type.Name.Substring(0, type.Name.Length - 2);
                typeName += OF;

                // Get type's arguments
                Type[] types = type.GetGenericArguments();
                foreach (Type t in types)
                {
                    // Append type's name
                    typeName += GetTypeName(t);
                }
            }
            else if (type.IsArray)
            {
                // Compose array name as "ArrayOf" and get array element's type name
                typeName = type.BaseType.Name;
                typeName += OF;
                typeName += GetTypeName(type.GetElementType());
            }
            else
            {
                // Append type's name
                typeName = type.Name;
            }

            // return type's name
            return typeName;
        }
    }
}

Serializing Generics - SerializableQueue<T>

The SerializableQueue class extends Queue<T> and implements IXmlSerializable, allowing you to serialize a generic queue into XML:


/*
 * SerializableGenerics
 * Copyright (c) 2009, Eduardo Sanchez-Ros
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;

namespace SerializableGenerics
{
    /// <summary>
    /// Represents a first-in, first-out serializable collection of objects.
    /// </summary>
    /// <typeparam name="T">The type of the items on the queue.</typeparam>
    public class SerializableQueue<T> : Queue<T>, IXmlSerializable
    {
        // store list type
        private readonly Type m_type = typeof(T);

        /// <summary>
        /// Returns a string that represents the current SerializableDictionary.
        /// </summary>
        /// <returns>
        /// A string that represents the current SerializableDictionary.
        /// </returns>
        public override string ToString()
        {
            return SerializableGenerics.GetTypeName(GetType());
        }

        #region IXmlSerializable Members

        /// <summary>
        /// This property is reserved, apply the System.Xml.Serialization.XmlSchemaProviderAttribute
        /// to the class instead.
        /// </summary>
        /// <returns>
        /// An System.Xml.Schema.XmlSchema that describes the XML representation of the
        /// object that is produced by the System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)
        /// method and consumed by the System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)
        /// method.
        /// </returns>
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Generates an object from its XML representation.
        /// </summary>
        /// <param name="reader">The System.Xml.XmlReader stream from which the object is deserialized.</param>
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            // Create xml serializer for type
            XmlSerializer typeSerializer = new XmlSerializer(m_type);

            // Read start element and move to content
            reader.ReadStartElement();
            reader.MoveToContent();

            // Loop through elements
            while (reader.NodeType != XmlNodeType.EndElement)
            {
                // Deserialize type
                T item = (T)typeSerializer.Deserialize(reader);

                // Enqueue item on queue
                Enqueue(item);
            }

            // Read end element and move to content
            reader.ReadEndElement();
            reader.MoveToContent();
        }

        /// <summary>
        /// Converts an object into its XML representation.
        /// </summary>
        /// <param name="writer">The System.Xml.XmlWriter stream to which the object is serialized.</param>
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            // Create xml serializer for type
            XmlSerializer typeSerializer = new XmlSerializer(m_type);

            IEnumerator<T> enumerator = GetEnumerator();
            while (enumerator.MoveNext())
            {
                // Serialize type
                typeSerializer.Serialize(writer, enumerator.Current);
            }
        }

        #endregion
    }
}

The previous class uses the SerializableGenerics helper class which constructs the element name based on the key-value pair node.

Serializing Generics - SerializableLinkedList<T>

The SerializableLinkedList<T> class extends LinkedList<T> and implements IXmlSerializable, allowing you to serialize a generic linked list into XML:

/*
 * SerializableGenerics
 * Copyright (c) 2009, Eduardo Sanchez-Ros
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;

namespace SerializableGenerics
{
    /// <summary>
    /// Represents a strongly typed serializable list of objects that can be accessed by index.
    /// </summary>
    /// <typeparam name="T">The type of the items on the linked list.</typeparam>
    public class SerializableLinkedList<T> : LinkedList<T>, IXmlSerializable
    {
        // store list type
        private readonly Type m_type = typeof(T);

        /// <summary>
        /// Returns a string that represents the current SerializableDictionary.
        /// </summary>
        /// <returns>
        /// A string that represents the current SerializableDictionary.
        /// </returns>
        public override string ToString()
        {
            return SerializableGenerics.GetTypeName(GetType());
        }

        #region IXmlSerializable Members

        /// <summary>
        /// This property is reserved, apply the System.Xml.Serialization.XmlSchemaProviderAttribute
        /// to the class instead.
        /// </summary>
        /// <returns>
        /// An System.Xml.Schema.XmlSchema that describes the XML representation of the
        /// object that is produced by the System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)
        /// method and consumed by the System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)
        /// method.
        /// </returns>
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Generates an object from its XML representation.
        /// </summary>
        /// <param name="reader">The System.Xml.XmlReader stream from which the object is deserialized.</param>
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            // Create xml serializer for type
            XmlSerializer typeSerializer = new XmlSerializer(m_type);

            // Read start element and move to content
            reader.ReadStartElement();
            reader.MoveToContent();

            // Loop through elements
            while (reader.NodeType != XmlNodeType.EndElement)
            {
                // Deserialize type
                T value = (T)typeSerializer.Deserialize(reader);

                // Create a liked list node of T
                LinkedListNode<T> node = new LinkedListNode<T>(value);

                // Add node to linked list
                AddLast(node);
            }

            // Read end element and move to content
            reader.ReadEndElement();
            reader.MoveToContent();
        }

        /// <summary>
        /// Converts an object into its XML representation.
        /// </summary>
        /// <param name="writer">The System.Xml.XmlWriter stream to which the object is serialized.</param>
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            // Create xml serializer for type
            XmlSerializer typeSerializer = new XmlSerializer(m_type);

            IEnumerator<T> enumerator = GetEnumerator();
            while (enumerator.MoveNext())
            {
                // Serialize type
                typeSerializer.Serialize(writer, enumerator.Current);
            }
        }

        #endregion
    }
}

The previous class uses the SerializableGenerics helper class which constructs the element name based on the key-value pair node.

Thursday 24 March 2011

Serializing Generics - SerializableDictionary<T>

The SerializableDictionary<T> class extends Dictionary<T> and implements IXmlSerializable, allowing you to serialize a generic dictionary into XML. The code was first inspired on Paul Welter's article XML Serializable Generic Dictionary:

/*
 * SerializableGenerics
 * Copyright (c) 2009, Eduardo Sanchez-Ros
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;

namespace SerializableGenerics
{
    /// <summary>
    /// Represents a serializable collection of keys and values.
    /// </summary>
    /// <typeparam name="TKey">The type of the keys on the dictionary.</typeparam>
    /// <typeparam name="TValue">The type of the values on the dictionary.</typeparam>
    public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
    {
        // store key and value types
        private readonly Type m_tKey = typeof(TKey);
        private readonly Type m_tValue = typeof(TValue);

        /// <summary>
        /// Returns a string that represents the current SerializableDictionary.
        /// </summary>
        /// <returns>
        /// A string that represents the current SerializableDictionary.
        /// </returns>
        public override string ToString()
        {
            return SerializableGeneric.GetTypeName(GetType());
        }

        #region IXmlSerializable Members

        /// <summary>
        /// This property is reserved, apply the System.Xml.Serialization.XmlSchemaProviderAttribute
        /// to the class instead.
        /// </summary>
        /// <returns>
        /// An System.Xml.Schema.XmlSchema that describes the XML representation of the
        /// object that is produced by the System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)
        /// method and consumed by the System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)
        /// method.
        /// </returns>
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Generates an object from its XML representation.
        /// </summary>
        /// <param name="reader">The System.Xml.XmlReader stream from which the object is deserialized.</param>
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            // Create xml serializers for key and value
            XmlSerializer keySerializer = new XmlSerializer(m_tKey);
            XmlSerializer valueSerializer = new XmlSerializer(m_tValue);

            // Get key-value pair name
            string keyValuePairName = SerializableGeneric.GetKeyValuePairName(m_tKey, m_tValue);

            // Read start element and move to content
            reader.ReadStartElement();
            reader.MoveToContent();

            // Is keyValuePairName the start element
            if (!reader.IsStartElement(keyValuePairName))
            {
                // Throw an exception
                throw new XmlException("Starting element " + keyValuePairName + " not found.");
            }

            // Loop through key-value pairs
            while (reader.IsStartElement(keyValuePairName))
            {
                // Read key-value pair and move to content
                reader.ReadStartElement(keyValuePairName);
                reader.MoveToContent();

                // Deserialize key and value
                TKey key = (TKey)keySerializer.Deserialize(reader);
                TValue value = (TValue)valueSerializer.Deserialize(reader);

                // Read end element and add key-value pair to dictionary
                reader.ReadEndElement();
                Add(key, value);
            }

            // Read end element and move to content
            reader.ReadEndElement();
            reader.MoveToContent();
        }

        /// <summary>
        /// Converts an object into its XML representation.
        /// </summary>
        /// <param name="writer">The System.Xml.XmlWriter stream to which the object is serialized.</param>
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            // Create xml serializers for key and value
            XmlSerializer keySerializer = new XmlSerializer(m_tKey);
            XmlSerializer valueSerializer = new XmlSerializer(m_tValue);

            // Get key-value pair name
            string keyValuePairName = SerializableGeneric.GetKeyValuePairName(m_tKey, m_tValue);

            Enumerator enumerator = GetEnumerator();
            while (enumerator.MoveNext())
            {
                // Get current key value pair
                KeyValuePair<TKey, TValue> keyValuePair = enumerator.Current;

                // Write start element with key-value pair name 
                writer.WriteStartElement(keyValuePairName);

                // Serialize key and value
                keySerializer.Serialize(writer, keyValuePair.Key);
                valueSerializer.Serialize(writer, keyValuePair.Value);

                // Write end element with key-value pair name
                writer.WriteEndElement();
            }
        }

        #endregion
    }
}

As an example, let's say we have a Serializable Dictionary of Strings and String Arrays. The type will be declared as follows:

SerializableDictionary<String, String[]>

And the resulting serialized XML:


    
        Key #0
        
            Value 1 of 0
            Value 2 of 0
            Value 3 of 0
        
    
    
        Key #1
        
            Value 1 of 1
            Value 2 of 1
            Value 3 of 1