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