Thursday, 29 December 2016

Java syntax highlight in Blogger

SyntaxHighlighter is one of the most widely used syntax highlighter for webs and blogs. It is an open source Java Script client side library that adds an IDE look and feel view to your code snippets. 
The documentation for version 3.0.83 (latest version at the time of writing this post) can be found here.

Using SyntaxHighlighter 

Before making any changes, please take a backup of your Blogger template you want to customize. Paste the following code before </head> tag in the HTML of your template and save the changes.
    
<link href="http://agorbatchev.typepad.com/pub/sh/3_0_83/styles/shCore.css" rel="stylesheet" type="text/css" />
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shCore.js" type="text/javascript" />
<link href="http://agorbatchev.typepad.com/pub/sh/3_0_83/styles/shThemeDefault.css" rel="stylesheet" type="text/css" />
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushJava.js" type="text/javascript" />

<script language="javascript" type="text/javascript">
 SyntaxHighlighter.config.bloggerMode = true;
 SyntaxHighlighter.config.clipboardSwf = "http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/clipboard.swf";
 SyntaxHighlighter.all();
</script>

First I added the base files shCore.css and shCore.js needed for the syntax highlighter to work. Then I chose the default theme shThemeDefault.css that comes with white background. Finally, I added the syntax highlighter for Java (shBrushJava.js).

Note that I used a specific version of the library here. Most examples I have seen point to the current version under http://agorbatchev.typepad.com/pub/sh/current, which is fine. However, if you have the HTTPS redirection enabled in your blog, the syntax highlight will not work properly as the Blogger engine will try to get the files from https://agorbatchev.typepad.com/pub/sh/current, which is not available throwing a 404 error.

After we configured the library in the template's head section we have to options make use of the functionality:

pre method: It is the most reliable of the methods as it works everywhere. The only drawback is that any code within the tag has to be escaped.
script method: If we don't want to escape your code we can use this method. Inside the CDATA we can put anything. The problem with script is that it's not supported everywhere, including Blogger.

We will make use of the pre method here. Let's add a pre block with the escaped Java code snippet in your post HTML editor:
<pre class="brush: java">public static void printPersonsWithinAgeRange(
    List&lt;Person&gt; roster, int low, int high) {
    for (Person p : roster) {
        if (low &lt;= p.getAge() &amp;&amp; p.getAge() &lt; high) {
            p.printPerson();
        }
    }
}
</pre>
After we save the changes we should see the code highlighted in the post preview:
public static void printPersonsWithinAgeRange(
    List<Person> roster, int low, int high) {
    for (Person p : roster) {
        if (low <= p.getAge() && p.getAge() < high) {
            p.printPerson();
        }
    }
}

Other Themes

There are a few themes available in SyntaxHighlighter which can completely change the look and feel of the highlighted syntax by including the desired CSS. For more information about the themes visit the themes page.

If you want to try them out click on the link to theme:


Available Brushes

Pretty much all main programming and scripting languages are supported by the library. We just have to include the brushes we want to use in the head section. Here is a list of the ones supported: 

    
<!-- Action Script 3  -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushAS3.js" type="text/javascript" />
<!-- Bash/Shell -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushBash.js" type="text/javascript" />
<!-- Cold Fusion -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushColdFusion.js" type="text/javascript" />
<!-- C# -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushCSharp.js" type="text/javascript" />
<!-- C++ -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushCpp.js" type="text/javascript" />
<!-- CSS -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushCss.js" type="text/javascript" />
<!-- Delphi -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushDelphi.js" type="text/javascript" />
<!-- Diff -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushDiff.js" type="text/javascript" />
<!-- Erlang -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushErlang.js" type="text/javascript" />
<!-- Groovy -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushGroovy.js" type="text/javascript" />
<!-- JavaScript -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushJScript.js" type="text/javascript" />
<!-- JavaFX -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushJavaFX.js" type="text/javascript" />
<!-- Perl -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushPerl.js" type="text/javascript" />
<!-- PHP -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushPhp.js" type="text/javascript" />
<!-- Plain Text -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushPlain.js" type="text/javascript" />
<!-- Power Shell -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushPowerShell.js" type="text/javascript" />
<!-- Python -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushPython.js" type="text/javascript" />
<!-- Ruby -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushRuby.js" type="text/javascript" />
<!-- Scala -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushScala.js" type="text/javascript" />
<!-- SQL -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushSql.js" type="text/javascript" />
<!-- Visual Basic -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushVb.js" type="text/javascript" />
<!-- XML -->
<script src="http://agorbatchev.typepad.com/pub/sh/3_0_83/scripts/shBrushXml.js" type="text/javascript" />

New version

If you want to try out the new version, the source code and documentation for SyntaxHighlighter can be found in GitHub. There is a 4.0.1 tag in the repository but we could not find a hosted version to refer to.  

Sunday, 27 May 2012

Getting started with Puppet

Puppet is a system for automating system administration tasks. You can find a brief introduction here.

Although it is difficult to get your head around Puppet, once you understand the architecture and get acquainted with the language you wish you learnt it earlier. Here are some material that will help you get up to speed with Puppet: 

Books

Source Code 


Documentation 

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
    }
}