Anatomy of Typical Packaging-Shipping Jobs
The easiest way to figure out how to package and ship, is to look at the jobs created for each retailer. However, as these tasks constitute the primary application use cases, a little more detail is in order
My hope was to create a system in which a minimum of new code needs
to be written; relying instead on prexisting tasks for operations such
as creating directories, tranforming media files, creating retailer specific manifest files, and sending files by ftp.
The normal workflow is to set up the job context, by placing
directives on the hash map passed to each job and then to use a common
skeleton for most common tasks. Packaging tasks in which separate directories are created for each
release can get by with just a ContextPreparer, and perhaps an IXMLTransformer for
carrying out operations on the XML Manifest files created for each
release or shipment as a whole. For example, the itunes packager adds a
task to the VendorManifestProducer to insert a file hashes for all the
media into the xml file.
Packaging
IExtractor
A packaging job first calls a IExtractor task that creates the shipments it is to act on. See Job System Workflow
ContextPreparer
Before anything else can happen the job context should be set . This is done by a class usually called the ContextPreparer.
Almost every retailer specific job includes on of these, and by
convention we've been putting all retailer specific classes in a
package named comp.idea.imp.service.retailername. In what follows you'll describe all the tasks and what must be done in the ContextPreparer to set them up.
.
FileSystemStructureBuilder
After
setting up the context, the FileSystemStructureBuilder task is usually
called, this creates all the required directories for a shipment based
in information on the context. This is what the connect packager
ContextPreparer does in the visitShipment method to create the root shipment directory:
PackagerHelpers. addObjectToContext (decoratedObject, PackagerHelpers.CREATE_DIR, shipmentDirName);
It does something similar in the visitRelease method for each Release.
The
napster packager works a little differently in that it creates several
directories per shipment. It aggregates the media files in a single
directory, same for the graphics, and creates a single XML file in yet
another directory for each shipment. It uses the visitShipment method
to decorate the Shipment object in such a way that multiple directories
are created:
PackagerHelpers.addMultipleToContext(decoratedObject,PackagerHelpers.CREATE_DIR, shipmentDirName);
PackagerHelpers.addMultipleToContext(decoratedObject,PackagerHelpers.CREATE_DIR,xmlDir); PackagerHelpers.addMultipleToContext(decoratedObject,PackagerHelpers.CREATE_DIR,graphicsDir );
PackagerHelpers.addMultipleToContext(decoratedObject,PackagerHelpers.CREATE_DIR, audioDir);
Source Media File Decoding
Most
packagers require the source media to be decoded from flac to wav
format for encoding. This done by including the
following definition in the job:
<bean class= "com.idea.imp.service.packager.TemporaryFileDecoder">
<constructor-arg><ref bean="flacDecoder"/></constructor-arg>
<property name="stopProcessingOnFailure">
<value>true</value>
</property>
</bean>
Media File Creation
Music files are shipped to every retailer. Often they require multiple files with different quality encodings, lengths (for preview), and even formats. So far encodors have been created for:- wma files
- atrac files
- aac files
- mp3 files
- flac files
<bean class="com.idea.imp.service.packager.TrackTransformerTask>
<constructor-arg><ref bean="wmaEncoder"/></constructor-arg>
<constructor-arg><value>%cmdExtra% -silent -input %in%
-a_setting 192_44_2 -output %out%</value></constructor-arg>
<constructor-arg><value>_009.wma</value></constructor-arg>
<!--Task is run if packager specific key found on context-->
</bean>
<bean class="com.idea.imp.service.packager.TrackTransformerTask">
<constructor-arg><ref bean="wmaEncoder"/></constructor-arg>
<constructor-arg><value>%cmdExtra% -silent -input %in%
-profile a128 -output %out%</value></constructor-arg>
<constructor-arg><value>_007.wma</value></constructor-arg>
<!--Task is run if packager specific key found on context-->
</bean>
In the ContextPreparer, these are set up like this, notice additional information can be passed for command line switches under the orginal key with a PackagerHelpers.CMD_Extra appended:
Consult the ContextPreparer source code for the connect, itunes, and napster packagers (the test cases can also be helpful). You'll find examples for all sorts of operations, different bit rates, providing metadata, encoding only a portion of a file, etc.public static final String _128 = "_007.wma";
public static final String _192 = "_009.wma";
PackagerHelpers.addObjectToContext(decoratedObject, _128, targetfileBase + _128 );
PackagerHelpers.addObjectToContext(decoratedObject,
_128 + PackagerHelpers.CMD_EXTRA, cmdExtra);PackagerHelpers.addObjectToContext(decoratedObject, _192, targetfileBase + _192);
PackagerHelpers.addObjectToContext(decoratedObject,
_192 + PackagerHelpers.CMD_EXTRA, cmdExtra);
Graphic File Encoding
This is just a variation on file encoding (ideed at bottom it's the same class that does the work). Typically, this is a matter of changing the size and color/bit depth of the output file. If you do not specify a context key for the ExternalMediaTransformerTask it will pick up the information from the following key in the job context: PackagerHelpers.EXTERNALMEDIATRANFORMER_TASK. This is usually sufficient since there is usually one image per release. You can generate multiple images by declaring multiple ExternalMediaTransformerTasks each keyed appropriately (as with the TrackTransformerTasks where multiple files are normal).VendorManifestProducer
This task has a list of subtasks, all of which implement the IXMLTransformer interface. It is an xml pipeline with the results of each tranformation being fed to the next. First it marshalls an xml representation of a complete shipment graph using Castor. Then, carries out an XSL tranform that creates a retailer specific manifest file. Other tranforms can be inserted into this pipeline to further massage the xml manifest file. The last transform should be the DocumentSerializer which writes the manifest file disk. Two things need to be done to set up this task.Any xsl parameters required by the transforms must be put under the context. Up until now this has been done in the visitShipment method like so:
context.put(XSLTransformer.class.getSimpleName(),someProps);The name of the xml file to be written must also be specified. Here's an example of a task that is set up to create an xml file for each release. It does this in the visitRelease method:
PackagerHelpers.addObjectToContext(decoratedObject,
DocumentSerializer.class.getSimpleName(),
currentReleaseDirectory + File.separator + aRelease.getUPC() + ".xml");
PackagerFinalizer
As of May 2006, packagers preparing releases for shipment and that create separate directories for each release can just rely on the the default com.idea.imp.service.packager.PackagerFinalizer. When creating a packager that aggregates releases look at the one in the napster package.
PackagerFinalizer examines the state of each shipment, removes releases that failed packaging, sets error states etc.