# ServingXML: Extending

### How to Extend the ServingXML Mark-Up Vocabulary

`ServingXML` works as an "inversion of control" container for assembling components and making them work together to process records and XML. New elements may be added by implementing the contracts in the extension API's. See Serving XML: Inversion of Control Container for an overview.

## Record Readers, Writers, and Filters

This section provides templates for custom record reader, filter, and writer elements that can be used in ServingXML resources scripts, as illustrated below.

    <sx:resources xmlns:sx="http://www.servingxml.com/core"
                  xmlns:myns="myurl">

      <sx:service id="countries">
        <sx:recordStream>
          <sx:recordReader ref="myFileReader"/>
          <myns:myRecordFilter myAttribute="xxx"/>
          <myns:myRecordWriter myAttribute="xxx"/>
          <sx:discardHandler>
            <sx:log message="{$sx:message}"/>
          </sx:discardHandler>
        </sx:recordStream>
      </sx:service>

      <sx:flatFileReader id="myFileReader">
        <sx:flatFile ref="countriesFile"/>
      </sx:flatFileReader>

    </sx:resources>

### Record Readers

#### Assembler

    package mypackage;

    import com.servingxml.app.ParameterDescriptor;
    import com.servingxml.components.recordio.RecordReaderFactory;
    import com.servingxml.components.recordio.RecordReaderFactoryPrefilter;
    import com.servingxml.util.ServingXmlException;

    public class MyRecordReaderFactoryAssembler {
      private ParameterDescriptor[] parameterDescriptors = ParameterDescriptor.EMPTY_ARRAY;
      private String myAttribute = "myDefault";

      public void setMyAttribute(String myAttribute) {
        this.myAttribute = myAttribute;
      }

      public void injectComponent(ParameterDescriptor[] parameterDescriptors) {
        this.parameterDescriptors = parameterDescriptors;
      }

      public RecordReaderFactory assemble(ConfigurationContext context) {
        RecordReaderFactory readerFactory = new MyRecordReaderFactory(/* args */);
        if (parameterDescriptors.length > 0) {
          readerFactory = new RecordReaderFactoryPrefilter(readerFactory,parameterDescriptors);
        }

        return readerFactory;
      }
    }

#### RecordReaderFactory

    package mypackage;

    import com.servingxml.app.Flow;
    import com.servingxml.app.ServiceContext;
    import com.servingxml.components.recordio.AbstractRecordReaderFactory;
    import com.servingxml.components.recordio.RecordReader;
    import com.servingxml.util.record.Record;
    import com.servingxml.util.ServingXmlException;

    public class MyReaderFactory extends AbstractRecordReaderFactory
    implements RecordReaderFactory  {

      public MyReaderFactory(/* args */) {
      }

      protected RecordReader createRecordReader(ServiceContext context, Flow flow) {

        RecordReader recordReader = new MyRecordReader(/* args */);
        return recordReader;
      }
    }

#### RecordReader

    package mypackage;

    import com.servingxml.app.Flow;
    import com.servingxml.app.ServiceContext;
    import com.servingxml.components.recordio.AbstractRecordReader;
    import com.servingxml.components.recordio.RecordReader;
    import com.servingxml.util.record.Record;
    import com.servingxml.util.ServingXmlException;

    public class MyReaderReader extends AbstractRecordReader
    implements RecordReader  {

      //  Per pipeline execution state

      public MyReaderReader(/* args */) {
      }

      public void readRecords(final ServiceContext context, final Flow flow) {
        try {
          startRecordStream(context, flow);
          //  For each record
          {
            RecordBuilder recordBuilder = new RecordBuilder();
            //  Build record
            Record newRecord = recordBuilder.toRecord();
            Flow newFlow = flow.replaceRecord(context, newRecord, ++lineNumber);
            try {
              writeRecord(context, newFlow);
            } catch (ServingXmlException e) {
              super.discardRecord(context, newFlow, e);
            }
          }
          endRecordStream(context, flow);
        } finally {
          close();
        }
      }
    }

#### components.xml

    <ioc:components xmlns:ioc="http://www.servingxml.com/ioc"
                    xmlns:sx="http://www.servingxml.com/core">
                    xmlns:myns="myurl">

      <ioc:serviceComponent name="myns:myRecordReader" base="sx:recordReader"
                    assemblerClass="mypackage.MyRecordReaderFactoryAssembler"/>
      ...

    </ioc:components>

### Record Writers

#### Assembler

    package mypackage;

    import com.servingxml.app.ParameterDescriptor;
    import com.servingxml.components.recordio.RecordWriterFactory;
    import com.servingxml.components.recordio.RecordWriterFactoryPrefilter;
    import com.servingxml.util.ServingXmlException;

    public class MyRecordWriterFactoryAssembler {
      private ParameterDescriptor[] parameterDescriptors = ParameterDescriptor.EMPTY_ARRAY;
      private String myAttribute = "myDefault";

      public void setMyAttribute(String myAttribute) {
        this.myAttribute = myAttribute;
      }

      public void injectComponent(ParameterDescriptor[] parameterDescriptors) {
        this.parameterDescriptors = parameterDescriptors;
      }

      public RecordWriterFactory assemble(ConfigurationContext context)  {

        RecordWriterFactory recordWriterFactory = new MyRecordWriterFactory(/* args */);
        if (parameterDescriptors.length > 0) {
          recordWriterFactory = new RecordWriterFactoryPrefilter(recordWriterFactory,parameterDescriptors);
        }
        return recordWriterFactory;
      }
    }

#### RecordWriterFactory

    package mypackage;

    import com.servingxml.app.Flow;
    import com.servingxml.app.ServiceContext;
    import com.servingxml.components.recordio.AbstractRecordWriterFactory;
    import com.servingxml.components.recordio.RecordWriter;
    import com.servingxml.util.record.Record;
    import com.servingxml.util.ServingXmlException;

    public class MyRecordWriterFactory extends AbstractRecordWriterFactory
    implements RecordWriterFactory {

      public MyRecordWriterFactory(/* args */) {
      }

      public RecordWriter createRecordWriter(ServiceContext context, Flow flow)
       {
        RecordWriter recordWriter = new MyRecordWriter();
        return recordWriter;
      }
    }

#### RecordWriter

    package mypackage;

    import com.servingxml.app.Flow;
    import com.servingxml.app.ServiceContext;
    import com.servingxml.components.recordio.AbstractRecordWriter;
    import com.servingxml.components.recordio.RecordWriter;
    import com.servingxml.util.record.Record;
    import com.servingxml.util.ServingXmlException;

    public class MyRecordWriter extends AbstractRecordWriter
    implements RecordWriter {

      //  Per pipeline execution state

      public MyRecordWriter(/* args */) {
      }

      public void startRecordStream(ServiceContext context, Flow flow)  {
        //  Set up state
      }

      public void writeRecord(ServiceContext context, Flow flow) {

        Record record = flow.getRecord();

        //  Write the record
      }

      public void endRecordStream(ServiceContext context, Flow flow) {
        //  Tear down state
      }

      public void close()  {
      // Close all remaining open resources.  The framework will call close()
      // even if there is a fault, so close() may be called even if startRecordStream()
      // has not been called,  or in the successful case, after resources have already
      // been disposed of in endRecordStream().
      }
    }

#### components.xml

    <ioc:components xmlns:ioc="http://www.servingxml.com/ioc"
                    xmlns:sx="http://www.servingxml.com/core"
                    xmlns:myns="myurl">

      <ioc:serviceComponent name="myns:myRecordWriter" base="sx:recordWriter"
                    assemblerClass="mypackage.MyRecordWriterFactoryAssembler"/>
      ...

    </ioc:components>

### Record Filters

#### Assembler

    package mypackage;

    import com.servingxml.app.ParameterDescriptor;
    import com.servingxml.components.recordio.RecordFilterAppender;
    import com.servingxml.components.recordio.RecordFilterAppenderPrefilter;
    import com.servingxml.util.ServingXmlException;

    public class MyRecordFilterAppenderAssembler {
      private ParameterDescriptor[] parameterDescriptors = ParameterDescriptor.EMPTY_ARRAY;
      private String myAttribute = "myDefault";

      public void setMyAttribute(String myAttribute) {
        this.myAttribute = myAttribute;
      }

      public void injectComponent(ParameterDescriptor[] parameterDescriptors) {
        this.parameterDescriptors = parameterDescriptors;
      }

      public RecordFilterAppender assemble(ConfigurationContext context) {
        RecordFilterAppender recordFilterAppender = new MyRecordFilterAppender(/* args */);
        if (parameterDescriptors.length > 0) {
          recordFilterAppender = new RecordFilterAppenderPrefilter(recordFilterAppender,parameterDescriptors);
        }
        return recordFilterAppender;
      }
    }

#### RecordFilterAppender

    package mypackage;

    import com.servingxml.app.Flow;
    import com.servingxml.app.ServiceContext;
    import com.servingxml.components.recordio.AbstractRecordFilterAppender;
    import com.servingxml.components.recordio.RecordFilterAppender;
    import com.servingxml.components.recordio.RecordPipeline;
    import com.servingxml.util.record.Record;
    import com.servingxml.util.ServingXmlException;

    public class MyRecordFilterAppender extends AbstractRecordFilterAppender
    implements RecordFilterAppender {

      public MyRecordFilterAppender(/* args */) {
      }

      public void appendToRecordPipeline(ServiceContext context, Flow flow, RecordPipeline pipeline) {
        RecordFilter filter = new MyRecordFilter();
        pipeline.addRecordFilter(filter);
      }
    }

#### RecordFilter

    package mypackage;

    import com.servingxml.app.Flow;
    import com.servingxml.app.ServiceContext;
    import com.servingxml.components.recordio.AbstractRecordFilter;
    import com.servingxml.util.RecordBuilder;
    import com.servingxml.util.ServingXmlException;

    public class MyRecordFilter extends AbstractRecordFilter
    implements RecordFilter  {

      //  Per pipeline execution state

      public MyRecordFilter(/* args */) {
      }

      public void startRecordStream(ServiceContext context, Flow flow) {
        //  Acquire resources (input streams, SQL connections, etc.)
        super.startRecordStream(context, flow);
      }

      public void writeRecord(ServiceContext context, Flow flow) {

        try {
          Record record = flow.getRecord();

          RecordBuilder recordBuilder = new RecordBuilder(record);
          //  Modify record
          Record newRecord = recordBuilder.toRecord();
          Flow newFlow = flow.replaceRecord(context, newRecord);

          super.writeRecord(context, newFlow);
        } catch (Exception e) {
          ServingXmlException reason = new ServingXmlException(e.getMessage(), e);
          super.discardRecord(context, flow, reason);
        }
      }

      public void endRecordStream(ServiceContext context, Flow flow) {
        //  Finish up
        super.endRecordStream(context, flow);
      }

      public void close()  {
        // Close all remaining open resources.  The framework will call close()
        // even if there is a fault, so close() may be called even if startRecordStream()
        // has not been called,  or in the successful case, after resources have already
        // been disposed of in endRecordStream().
      }
    }

#### components.xml

    <ioc:components xmlns:ioc="http://www.servingxml.com/ioc"
                    xmlns:sx="http://www.servingxml.com/core">
                    xmlns:myns="myurl">

      <ioc:serviceComponent name="myns:myRecordFilter" base="sx:recordFilter"
                    assemblerClass="mypackage.MyRecordFilterFactoryAssembler"/>
      ...

    </ioc:components>

## XML Serializers

This section provides templates for custom serializer elements that can be used in ServingXML resources scripts.

### Assembler

    package mypackage;

    import com.servingxml.app.ParameterDescriptor;
    import com.servingxml.components.property.OutputProperty;
    import com.servingxml.app.xmlpipeline.SerializerFactory;
    import com.servingxml.app.xmlpipeline.SerializerFactoryPrefilter;
    import com.servingxml.ioc.components.ConfigurationContext;
    import com.servingxml.util.ServingXmlException;

    public class MySerializerFactoryAssembler {
      private ParameterDescriptor[] parameterDescriptors = ParameterDescriptor.EMPTY_ARRAY;
      private OutputProperty[] outputProperties = new OutputProperty[0];

      public void injectComponent(ParameterDescriptor[] parameterDescriptors) {
        this.parameterDescriptors = parameterDescriptors;
      }

      public void injectComponent(OutputProperty[] outputProperties) {
        this.outputProperties = outputProperties;
      }

      public SaxSinkFactory assemble(ConfigurationContext context)  {

        SaxSinkFactory saxSinkFactory = new MySerializerFactory(outputProperties);
        if (parameterDescriptors.length > 0) {
          saxSinkFactory = new SerializerFactoryPrefilter(saxSinkFactory, parameterDescriptors);
        }

        return saxSinkFactory;
      }
    }

### SaxSinkFactory

    package mypackage;

    import java.util.Properties;
    import org.xml.sax.ContentHandler;

    import com.servingxml.app.Flow;
    import com.servingxml.app.ServiceContext;
    import com.servingxml.io.saxsink.SaxSink;
    import com.servingxml.app.xmlpipeline.SerializerFactory;
    import com.servingxml.util.ServingXmlException;

    public class MySerializerFactory implements SaxSinkFactory {

      public MySerializerFactory(/* args */) {
      }

      public Serializer createSerializer(ServiceContext context, Flow flow, Properties outputProperties) {

        ContentHandler handler = new MyContentHandler(/* args */);
        Serializer serializer = new MySerializer(handler, /* other args */);
        return serializer;
      }
    }

### Serializer

    package mypackage;

    import org.xml.sax.ContentHandler;

    import com.servingxml.io.saxsink.SaxSink;

    public class MySerializer implements SaxSink {
      private final ContentHandler handler;

      public MySerializer(ContentHandler handler, /* other args */) {
        this.handler = handler;
      }

      public ContentHandler getContentHandler() {
        return handler;
      }

      public void setOutputProperties(Properties outputProperties) {
        //  Called by the framework to set output properties on the SAX sink
      }

      public void close() {
        //  Close output streams
      }
    }

### components.xml

    <ioc:components xmlns:ioc="http://www.servingxml.com/ioc"
                    xmlns:sx="http://www.servingxml.com/core">
                    xmlns:myns="myurl">

      <ioc:serviceComponent name="myns:mySerializer" base="sx:saxSink"
                    assemblerClass="mypackage.MySerializerFactoryAssembler"/>
      ...

    </ioc:components>