JDF Blog

Tools, techniques and advice for real-world JDF integrations

First Look at Fluent JDF: Authoring JDF from Templates

with one comment

A couple days ago I began a series of posts on getting started with Fluent JDF, a complete opensource .NET library for building JDF client applications. In the first installment I showed you how Fluent JDF can help you build tickets from scratch. In this installment, I am going to show you the JDF-aware templating support built into Fluent JDF. I will once again be using LinqPad to demonstrate code. Read the first post in the series if you are unfamiliar with using LinqPad with Fluent JDF.

The idea is quite simple: Take a JDF ticket that works, turn it into a template and generate new, valid JDF tickets based on it. For example, consider the following intent template for a job that prints and side stitches:

<?xml version="1.0" encoding="UTF-8"?>
<JDF DescriptiveName="Print And Corner Stitch" ID="ID_20100406_092100"
  JobPartID="1" MaxVersion="1.4" Status="Waiting" Type="Product"
  Version="1.4" xmlns="http://www.CIP4.org/JDFSchema_1_1"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:type="Product">
  <ResourceLinkPool>
    <ComponentLink Usage="Output" rRef="OutputComponent" Amount="1000"/>
    <LayoutIntentLink Usage="Input" rRef="ID_20100438"/>
    <ArtDeliveryIntentLink Usage="Input" rRef="ID_20100486"/>
    <MediaIntentLink Usage="Input" rRef="ID_20100516"/>
    <BindingIntentLink Usage="Input" rRef="binding1"/>
  </ResourceLinkPool>
  <ResourcePool>
    <ArtDeliveryIntent Class="Intent" ID="ID_20100486" Status="Available">
      <ArtDelivery ArtDeliveryType="DigitalFile">
        <RunListRef rRef="ID_20100488"/>
      </ArtDelivery>
    </ArtDeliveryIntent>
    <LayoutIntent Class="Intent" ID="ID_20100438" Status="Available">
      <FinishedDimensions DataType="ShapeSpan" Preferred="612 792 0"/>
    </LayoutIntent>
    <MediaIntent Class="Intent" DescriptiveName="US Letter, White"
    ID="ID_20100516" Status="Available">
    </MediaIntent>
    <RunList Class="Parameter" ID="ID_20100488" Status="Available">
      <LayoutElement>
        <FileSpec URL="Staple.pdf"/>
      </LayoutElement>
    </RunList>
   <Component Class="Quantity" ComponentType="FinalProduct" Amount="1000"
      DescriptiveName="Green Sample" ID="OutputComponent" Status="Unavailable"/>
   <BindingIntent Class="Intent" ID="binding1" Status="Available">
    <BindingSide DataType="EnumerationSpan" Actual="Left"/>
    <BindingType DataType="EnumerationSpan" Actual="CornerStitch"/>
   </BindingIntent>
  </ResourcePool>
</JDF>

If you wanted to create a job from this, you’d be tempted to simply load it up and send it out.   However, in JDF terms there are a few things you’d need to do before it was truly valid:

  1. Generate a unique JobId
  2. Change Template=”true” to Template=”false”
  3. Make all the IDs unique
  4. Fix all references to the IDs you change

Not too hard, but kind of a pain if you do it yourself. However, if you are using Fluent JDF, it’s as simple as saving your template to a file and writing one line of code:

  InitializeFluentJdf();
  var ticket = Ticket.CreateFromTemplate(@"e:\downloads\Template.jdf").Generate();
  ticket.Dump();

I’ve highlighted the code used to load the template and create a ticket from it. Like last time, I’m including boilerplate library initialization code and a call to Dump() to output the results in LinqPad. The majority of the remaining examples will leave out the extra code.

Anyway, if you run this sample, you’ll get output like this:

<JDF DescriptiveName="Print And Corner Stitch" ID="TI_6bf35" JobID="J_628a0" Template="false" Status="Waiting" Type="Product" Version="1.4" xmlns="http://www.CIP4.org/JDFSchema_1_1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Product">
  <ResourceLinkPool>
    <ComponentLink Usage="Output" rRef="TI_c37d9" Amount="1000" />
    <LayoutIntentLink Usage="Input" rRef="TI_979cb" />
    <ArtDeliveryIntentLink Usage="Input" rRef="TI_0a2e4" />
    <MediaIntentLink Usage="Input" rRef="TI_73d24" />
    <BindingIntentLink Usage="Input" rRef="TI_28d5c" />
  </ResourceLinkPool>
  <ResourcePool>
    <ArtDeliveryIntent Class="Intent" ID="TI_0a2e4" Status="Available">
      <ArtDelivery ArtDeliveryType="DigitalFile">
        <RunListRef rRef="TI_ce198" />
      </ArtDelivery>
    </ArtDeliveryIntent>
    <LayoutIntent Class="Intent" ID="TI_979cb" Status="Available">
      <FinishedDimensions DataType="ShapeSpan" Preferred="612 792 0" />
    </LayoutIntent>
    <MediaIntent Class="Intent" DescriptiveName="US Letter, White" ID="TI_73d24" Status="Available"></MediaIntent>
    <RunList Class="Parameter" ID="TI_ce198" Status="Available">
      <LayoutElement>
        <FileSpec URL="Staple.pdf" />
      </LayoutElement>
    </RunList>
    <Component Class="Quantity" ComponentType="FinalProduct" Amount="1000" DescriptiveName="Green Sample" ID="TI_c37d9" Status="Unavailable" />
    <BindingIntent Class="Intent" ID="TI_28d5c" Status="Available">
      <BindingSide DataType="EnumerationSpan" Actual="Left" />
      <BindingType DataType="EnumerationSpan" Actual="CornerStitch" />
    </BindingIntent>
  </ResourcePool>
</JDF>

Notice how the job id is set, Template is set to false, new IDs have been generated and all the references are fixed. You now have a valid ticket ready to send into a JDF workflow. The other nice thing is this: If you generate another ticket, it will get it’s own job id and resource ids so it will be valid even if sent into the same workflow.

Often, you’ll want to control the JobID. That’s easy too:

var ticket = Ticket.CreateFromTemplate(@"e:\downloads\Template.jdf").With().JobId("theJobId").Generate();

In real life, you also need to change other attributes. For example, you’d probably need to provide a different PDF with each ticket. You’d do that by changing the URL attribute of the RunList referenced from the ArtDelivery:

<RunList Class="Parameter" ID="TI_ce198" Status="Available">
  <LayoutElement>
    <FileSpec URL="Staple.pdf" />
  </LayoutElement>
</RunList>

Start by making the URL’s value a replacement variable like this:

<RunList Class="Parameter" ID="TI_ce198" Status="Available">
  <LayoutElement>
    <FileSpec URL="[:url:]" />
  </LayoutElement>
</RunList>

Then provide a value in your code:

var ticket = Ticket.CreateFromTemplate(@"e:\downloads\Template.jdf")
  .With().NameValue("url", "http://myServer/file.pdf")
  .Generate();

To produce the RunList you want:

<RunList Class="Parameter" ID="TI_ce198" Status="Available">
  <LayoutElement>
    <FileSpec URL="http://myServer/file.pdf" />
  </LayoutElement>
</RunList>

The values for replacement do not have to be strings. For simple types, like int, float and so on, the ToString() method is called to get the replacement value. In the case of DateTime types, the value is formatted as a legal JDF date time. Consider the following template for a QueueEntryStatus query:

<JMF xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="1.4" TimeStamp="[:sentDateTime:]" xmlns="http://www.CIP4.org/JDFSchema_1_1">
  <Query ID="QES_f66eb" Type="QueueEntryStatus" xsi:type="QueryQueueEntryStatus" />
</JMF>

This is JMF so you have to use the Message class instead of the Ticket class. Otherwise, the code should look pretty familiar:

InitializeFluentJdf();
var message = Message.CreateFromTemplate(@"e:\downloads\template2.jdf")
  .With().NameValue("sentDateTime", DateTime.UtcNow)
  .Generate();
message.Dump();

Notice that we passed the current date time in UTC format. This gives us output with a UTC date time in correct JDF format. The “Z” at the end means it is a UTC date time:

<JMF xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="1.4" TimeStamp="2011-07-22T16:50:26.2245983Z" xmlns="http://www.CIP4.org/JDFSchema_1_1">
  <Query ID="TI_fd311" Type="QueueEntryStatus" xsi:type="QueryQueueEntryStatus" />
</JMF>

Local time works right too. For example, this code:

var message = Message.CreateFromTemplate(@"e:\downloads\template2.jdf")
  .With().NameValue("sentDateTime", DateTime.Now)
  .Generate();

Produces the time stamp with a local time offset, which in this case is -05:00:

<JMF xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="1.4" TimeStamp="2011-07-22T11:56:58.2320198-05:00" xmlns="http://www.CIP4.org/JDFSchema_1_1">
  <Query ID="TI_2a7bf" Type="QueueEntryStatus" xsi:type="QueryQueueEntryStatus" />
</JMF>

You can also use formulas to provide default values. Let’s revise the template to use the now() formula, which provides the current date and time in UTC format:

<JMF xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="1.4" TimeStamp="[:sentDateTime=now():]" xmlns="http://www.CIP4.org/JDFSchema_1_1">
  <Query ID="QES_f66eb" Type="QueueEntryStatus" xsi:type="QueryQueueEntryStatus" />
</JMF>

If we don’t supply a value for “sentDateTime”, the template engine will automatically supply one. So this code:

var message = Message.CreateFromTemplate(@"e:\downloads\template2.jdf").Generate();

Outputs a ticket with the current date time in the TimeStamp attribute:

<JMF xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="1.4" TimeStamp="2011-07-22T17:03:01.7058093Z" xmlns="http://www.CIP4.org/JDFSchema_1_1">
  <Query ID="TI_54895" Type="QueueEntryStatus" xsi:type="QueryQueueEntryStatus" />
</JMF>

Defaults can also be constants. For example, let’s make the version attribute a variable with a constant default:

<JMF xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="[:version=1.4:]" TimeStamp="[:sentDateTime=now():]" xmlns="http://www.CIP4.org/JDFSchema_1_1">
  <Query ID="QES_96237" Type="QueueEntryStatus" xsi:type="QueryQueueEntryStatus" />
</JMF>

If we do not supply a replacement for version:

var message = Message.CreateFromTemplate(@"e:\downloads\template2.jdf").Generate();

We get the default:

<JMF xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="1.4" TimeStamp="2011-07-22T17:07:16.4533800Z" xmlns="http://www.CIP4.org/JDFSchema_1_1">
  <Query ID="TI_da0c6" Type="QueueEntryStatus" xsi:type="QueryQueueEntryStatus" />
</JMF>

However, when a replacement value is supplied:

var message = Message.CreateFromTemplate(@"e:\downloads\template2.jdf")
  .With().NameValue("version", "1.3")
  .Generate();

The value supplied is used instead of the default:

<JMF xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="1.3" TimeStamp="2011-07-22T17:07:16.4533800Z" xmlns="http://www.CIP4.org/JDFSchema_1_1">
  <Query ID="TI_da0c6" Type="QueueEntryStatus" xsi:type="QueryQueueEntryStatus" />
</JMF>

There are a number of additional advanced features that I will cover in a future post including:

  • Replacing variables based on public properties of an object
  • IEnumerable/repeating section replacements
  • Custom formulas

In the meanwhile, feel free to ask questions over at our Codeplex project.

Advertisements

Written by Tom Cabanski

July 22, 2011 at 5:33 pm

Posted in FluentJDF

One Response

Subscribe to comments with RSS.

  1. I really would like to take note of this specific post, “First Look
    at Fluent JDF: Authoring JDF from Templates JDF Blog” on my
    personal site. Will you care in case Ido it?
    Thank you ,Marcy

    Candra

    February 20, 2013 at 4:30 am


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: