Project

Profile

Help

Bug #2339 » XslTransformation.cs

Class used for compiling. it's done in the constructor of the class. - Bob Williams, 2015-03-27 15:00

 
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.Practices.ObjectBuilder2;
using net.sf.saxon.lib;
using Saxon.Api;
using WKHMR.SSR.Common.Logging;
using WKHMR.SSR.ConfigurationServices.Common;
using WKHMR.SSR.Workflow.Shared.Job;
using WKHMR.SSR.WorkflowController.Transformation.Framework.Common;
using FileInfo = WKHMR.SSR.Workflow.Shared.Job.Models.FileInfo;

namespace WKHMR.SSR.WorkflowController.Transformation.Framework.Xslt
{
public class XslTransformation : IXslTransformation
{
private readonly IWipRepository _wipRepository;
private readonly IPipelineResultHandler _pipelineResultHandler;
private readonly XsltExecutable _executable;
private readonly XslStreamResolver _resolver;
private readonly ILoggingFactory _loggingFactory;
private readonly Processor _processor;

/// <summary>
/// Constructor for XslTransformation
/// </summary>
/// <param name="resolver">An XslStreamResolver that will resolve the main Xsl file specified in xslFileName and any files referenced by
/// the Xsl.
/// </param>
/// <param name="loggingFactory"></param>
/// <param name="xslFileName">The name of the main Xsl file</param>
/// <param name="wipRepository"></param>
/// <param name="pipelineResultHandler"></param>
public XslTransformation(XslStreamResolver resolver, ILoggingFactory loggingFactory, string xslFileName,
IWipRepository wipRepository, IPipelineResultHandler pipelineResultHandler) {
_wipRepository = wipRepository;
_pipelineResultHandler = pipelineResultHandler;
using (
var tracer = loggingFactory.CreateMethodTracer("Constructor", GetType(), resolver, loggingFactory,
xslFileName)) {
_loggingFactory = loggingFactory;
_resolver = resolver;
var stream = resolver.GetStream(xslFileName);
_processor = new Processor(true);
_processor.XmlResolver = resolver;

_processor.Implementation.setConfigurationProperty(FeatureKeys.GENERATE_BYTE_CODE, "false");


var compiler = _processor.NewXsltCompiler();
string baseUri = "file://" + xslFileName;
compiler.BaseUri = new Uri(baseUri);
compiler.ErrorList = new ArrayList();

try {
tracer.TraceMessage("Start XSL compile");
_executable = compiler.Compile(stream);
tracer.TraceMessage("End XSL compile");
}
catch (StaticError) {
if (compiler.ErrorList.Count > 0) {
var exception = compiler.ErrorList[0] as Exception;
if (exception != null) throw exception;
}
throw;
}
catch (Exception) {
_loggingFactory.GetDiagnosticLogger().Error("Error compiling XSL file(s) " + xslFileName);

throw;
}
}
}

/// <summary>
/// Executes the Xsl transform against the inputXml passing the manifestXml as a parameter. Writes the tranformed
/// Xml to outputXml. Used for backwards compatability for Books.
/// </summary>
/// <param name="withSchemaValidation"></param>
/// <param name="inputFile"></param>
/// <param name="outputFile"></param>
/// <param name="fileStorageManager"></param>
private FileStream ProcessTransform(bool withSchemaValidation, FileInfo inputFile, FileInfo outputFile, IFileStorageManager fileStorageManager) {
using (_loggingFactory.CreateMethodTracer("ProcessTransform", GetType())) {
_loggingFactory.GetDiagnosticLogger()
.Info(string.Format("Physical memory allocated before transformation {0}",
Process.GetCurrentProcess().WorkingSet64));

var destination = new Serializer();
var builder = _processor.NewDocumentBuilder();
var transformer = _executable.Load();

transformer.InputXmlResolver = _resolver;
//sets up the local file storage
fileStorageManager.CreateFolder();

//dump the input file into a file stream
//keep buffer low to keep RAM usage to a minimum
var inputFileStream = File.Create(string.Concat(fileStorageManager.FolderUri.AbsolutePath, "/", inputFile.FileName), 1024);
//dump the wip bytes to file stream
using (var wipInputStream = _wipRepository.GetFile(inputFile)) {
_loggingFactory.GetDiagnosticLogger()
.Info(string.Format("Physical memory allocated after input file bytes sent down from WIP {0}",
Process.GetCurrentProcess().WorkingSet64));
wipInputStream.Seek(0, SeekOrigin.Begin);
wipInputStream.CopyTo(inputFileStream);
inputFileStream.Flush(true);
inputFileStream.Position = 0;

var settings = new XmlReaderSettings {XmlResolver = _resolver, DtdProcessing = DtdProcessing.Parse};
using (var xmlReader = XmlReader.Create(inputFileStream, settings)) {
var input = builder.Build(xmlReader);
transformer.InitialContextNode = input;
}
}
//transformer.SetInputStream(inputFileStream, _baseUri);
_loggingFactory.GetDiagnosticLogger()
.Info(string.Format("Physical memory allocated after input for transformation set {0}",
Process.GetCurrentProcess().WorkingSet64));

//ToDo: Need to reconnect message listener when Saxon addresses the memory issue

//get file info for disk
var outputFolder = fileStorageManager.FolderUri.AbsolutePath;
var transformedFileToDisk = string.Concat(outputFolder, "/", outputFile.FileName);

if (!withSchemaValidation)
transformer.SetParameter(new QName("", "", "validation"), new XdmAtomicValue("no"));

//set dest location to file system
destination.SetOutputFile(transformedFileToDisk);

//execute transformation and send it to disk
//the xsl should give all nodes necessary to perform work
//ToDo: Xsl transformation should be parameter drive giving the xml structure needed by workflow
transformer.Run(destination);
//dispose of the filestream
inputFileStream.Dispose();
//put the file into the wip
//the FileStream will be disposed by the consumer.
var outputFileStream = new FileStream(transformedFileToDisk, FileMode.Open);

_wipRepository.SaveFile(outputFile, outputFileStream);

_loggingFactory.GetDiagnosticLogger()
.Info(string.Format("Physical memory allocated after transformation {0}",
Process.GetCurrentProcess().WorkingSet64));

outputFileStream.Position = 0;

return outputFileStream;
}
}


/// <summary>
/// Executes the Asset Xsl transform against the inputXml passing the request for change message as a parameter. Writes the tranformed
/// Xml to outputXml. Used by the transformation pipeline exclusively and for cases where all that is known is the file info.
/// </summary>
/// <param name="withSchemaValidation"></param>
/// <param name="fileStorageManager"></param>
/// <param name="command"></param>
/// <param name="producerManagerProxy"></param>
public void ProcessTransform(bool withSchemaValidation, IFileStorageManager fileStorageManager, PipelineCommand command, IProducerManagerProxy producerManagerProxy)
{
using (_loggingFactory.CreateMethodTracer("ProcessTransformPipeline", GetType()))
{

var destination = new Serializer();
var builder = _processor.NewDocumentBuilder();

var transformer = _executable.Load();

transformer.InputXmlResolver = _resolver;
Stream inputFileStream;

if (command.InputFile != null)
{
//sets up the local file storage
fileStorageManager.CreateFolder();
//dump the input file into a file stream
//keep buffer low to keep RAM usage to a minimum
inputFileStream = File.Create(string.Concat(fileStorageManager.FolderUri.AbsolutePath, "/", command.InputFile.FileName), 1024);
//dump the wip bytes to file stream

using (var wipInputStream = _wipRepository.GetFile(command.InputFile))
{
wipInputStream.Seek(0, SeekOrigin.Begin);
wipInputStream.CopyTo(inputFileStream);
((FileStream)inputFileStream).Flush(true);
inputFileStream.Position = 0;
}
}
else
{
inputFileStream = new MemoryStream(command.InputData);
}

//set the transformation key and feed it the producer manager
_pipelineResultHandler.Initialize(command.XslTransformationKey, producerManagerProxy, command.InputFile);
transformer.ResultDocumentHandler = _pipelineResultHandler;

var settings = new XmlReaderSettings { XmlResolver = _resolver, DtdProcessing = DtdProcessing.Parse };
using (var xmlReader = XmlReader.Create(inputFileStream, settings))
{
var input = builder.Build(xmlReader);
transformer.InitialContextNode = input;
}


if (!withSchemaValidation)
transformer.SetParameter(new QName("", "", "validation"), new XdmAtomicValue("no"));
//set all parameters
command.XslParameters.Where(x => x.Type == TransformationParameter.XSL_INTERNAL_PARAMETER).ForEach(x => transformer.SetParameter(new QName(x.Name), new XdmAtomicValue(x.Value)));


var responseStream = new MemoryStream();
//if the command type is not splitter then we add bytes to the producer manager.
var parameter =
command.XslParameters.FirstOrDefault(x => x.Type == TransformationParameter.XSL_OUTPUT_LOCATION);
if (parameter != null && parameter.Value == "Main")
{
destination.SetOutputStream(responseStream);
}
transformer.Run(destination);
//splitter is handled in the ResultDocumentHandler
if (parameter != null && parameter.Value == "Main")
{
using (var br = new BinaryReader(responseStream)) {
responseStream.Position = 0;
byte[] bytes = br.ReadBytes(Convert.ToInt32(responseStream.Length));
producerManagerProxy.AddWorkLoad(new TransformationResponse {
OutputData = bytes,
TransformationKey =
command.XslTransformationKey,
TransformationStatus =
TransformationStatus.SUCCESS,
AssetHref = command.AssetHref
});
}
}
responseStream.Dispose();

//dispose of the filestream
inputFileStream.Dispose();
//dispose file
fileStorageManager.Dispose();
}
}

/// <summary>
/// This will be used by the Asset Message XSL during the pipeline cartridge since it's behavior is different than the other xslt's
/// Xml to outputXml.
/// Used by the transformation pipeline exclusively for working with asset message
/// </summary>
/// <param name="withSchemaValidation"></param>
/// <param name="command"></param>
/// <param name="producerManagerProxy"></param>
public void ProcessTransform(bool withSchemaValidation, PipelineCommand command, IProducerManagerProxy producerManagerProxy)
{
using (_loggingFactory.CreateMethodTracer("ProcessTransformPipeline", GetType()))
{
//short circuit the method for journals. We don't need to transform at this point to reduce overhead
//we just return the input bytes to the producer manager
if (producerManagerProxy.AssetManifest.InputType.ContentType == InputContentType.JOURNAL) {
producerManagerProxy.AddWorkLoad(new TransformationResponse
{
OutputData = command.InputData,
TransformationKey =
command.XslTransformationKey,
TransformationStatus =
TransformationStatus.SUCCESS,
AssetHref = command.AssetHref
});
return;
}
var destination = new Serializer();
var builder = _processor.NewDocumentBuilder();

var transformer = _executable.Load();

transformer.InputXmlResolver = _resolver;
//add to the xml resolver the input that asset message xsl needs
var firstOrDefault = command.XslParameters.FirstOrDefault(x => x.Type == TransformationParameter.XSL_ASSET_MESSAGE_INPUT_FILE);
if (firstOrDefault != null) {
var inputMessageParameter =
firstOrDefault.Value;
_resolver.AddStream(inputMessageParameter, new MemoryStream(Encoding.UTF8.GetBytes(command.AssetUpdateMessage)));
}
else {
//this is default defined in xsl
_resolver.AddStream("AssetMessageRequest.xml", new MemoryStream(Encoding.UTF8.GetBytes(command.AssetUpdateMessage)));
}
Stream inputFileStream = new MemoryStream(command.InputData);
//set the transformation key and feed it the producer manager

_pipelineResultHandler.Initialize(command.XslTransformationKey, producerManagerProxy, command.InputFile);
transformer.ResultDocumentHandler = _pipelineResultHandler;

var settings = new XmlReaderSettings { XmlResolver = _resolver, DtdProcessing = DtdProcessing.Parse };
//this sets the input stream for xsl which will be asset xml
using (var xmlReader = XmlReader.Create(inputFileStream, settings))
{
var input = builder.Build(xmlReader);
transformer.InitialContextNode = input;
}


if (!withSchemaValidation)
transformer.SetParameter(new QName("", "", "validation"), new XdmAtomicValue("no"));
//set all parameters
command.XslParameters.Where(x => x.Type == TransformationParameter.XSL_INTERNAL_PARAMETER).ForEach(x => transformer.SetParameter(new QName(x.Name), new XdmAtomicValue(x.Value)));


var responseStream = new MemoryStream();
//if the command type is not splitter then we add bytes to the producer manager.
var parameter =
command.XslParameters.FirstOrDefault(x => x.Type == TransformationParameter.XSL_OUTPUT_LOCATION);
if (parameter != null && parameter.Value == "Main")
{

destination.SetOutputStream(responseStream);

}
transformer.Run(destination);
responseStream.Position = 0;
//splitter is handled in the ResultDocumentHandler
if (parameter != null && parameter.Value == "Main")
{
using (var br = new BinaryReader(responseStream))
{
responseStream.Position = 0;
byte[] bytes = br.ReadBytes(Convert.ToInt32(responseStream.Length));
producerManagerProxy.AddWorkLoad(new TransformationResponse
{
OutputData = bytes,
TransformationKey =
command.XslTransformationKey,
TransformationStatus =
TransformationStatus.SUCCESS,
AssetHref = command.AssetHref
});
}

}
responseStream.Dispose();

//dispose of the filestream
inputFileStream.Dispose();
}
}

/// <summary>
/// This will be used by the Asset Message READ/UPDATE and maybe used outside the pipeline cartridge since its behavior is different than the other xslt's
/// Xml to outputXml.
/// Can be used outside the transformation pipeline for working with asset message
/// </summary>
/// <param name="withSchemaValidation"></param>
/// <param name="inputData"></param>
/// <param name="requestMessage"></param>
public Stream ProcessTransform(bool withSchemaValidation, byte[] inputData, string requestMessage) {
using (_loggingFactory.CreateMethodTracer("ProcessTransformPipeline", GetType())) {
var destination = new Serializer();
var builder = _processor.NewDocumentBuilder();

var transformer = _executable.Load();

transformer.InputXmlResolver = _resolver;

//this is default defined in xsl
_resolver.AddStream("AssetMessageRequest.xml",
new MemoryStream(Encoding.UTF8.GetBytes(requestMessage)));

Stream inputFileStream = new MemoryStream(inputData);
var settings = new XmlReaderSettings {XmlResolver = _resolver, DtdProcessing = DtdProcessing.Parse};
//this sets the input stream for xsl which will be asset xml
using (var xmlReader = XmlReader.Create(inputFileStream, settings)) {
var input = builder.Build(xmlReader);
transformer.InitialContextNode = input;
}


if (!withSchemaValidation)
transformer.SetParameter(new QName("", "", "validation"), new XdmAtomicValue("no"));

var responseStream = new MemoryStream();

destination.SetOutputStream(responseStream);

transformer.Run(destination);

responseStream.Position = 0;

return responseStream;
}
}


/// <summary>
/// Transforms the inputXml and writes to the outputXml
/// </summary>
/// <param name="inputFile"></param>
/// <param name="outputFile"></param>
/// <param name="fileStorageManager"></param>
/// <returns></returns>
public FileStream ProcessTransform(FileInfo inputFile, FileInfo outputFile, IFileStorageManager fileStorageManager) {
return ProcessTransform(true, inputFile, outputFile, fileStorageManager);
}
}
}
(4-4/4)