Kinesis SoftwareKineticFusion

[Show Table of Contents]

5 Using The Server Component

[Hide Table of Contents]



5.2 Processing SWF Movies

The KineticFusionServerType interface provides the following methods for translating SWF movies to RVML:

    /**
     * Process SWF input stream to an RVML and write RVML to output stream. An
     * RVML document is created and written to the specified output stream.
     * 
     * @param swfInput
     *            Provides input stream and additional information on the input
     *            SWF movie
     * @param rvmlStreamOutput
     *            Provides output stream and additional information on the output
     *            RVML document
     * @return true if the operation was successful, false
     *         otherwise.
     * @throws KineticFusionException Wraps all embedded exceptions.
     */
    public boolean processSingleSWFMovie(SWFInputType swfInput, 
                                         RVMLStreamOutputType rvmlStreamOutput) 
                                             throws KineticFusionException;


    /**
     * Process SWF input stream to an RVML and write RVML to output stream. An
     * RVML document is created and written to the specified output stream.
     * 
     * @param swfInput
     *            Provides input stream and additional information on the input
     *            SWF movie
     * @param rvmlSAXOutput
     *            Provides SAX content handler and additional information on the output
     *            RVML document
     * @return true if the operation was successful, false
     *         otherwise.
     * @throws KineticFusionException Wraps all embedded exceptions.
     */
    public boolean processSingleSWFMovie(SWFInputType swfInput, 
                                         RVMLSAXOutputType rvmlSAXOutput) 
                                            throws KineticFusionException;

Each method takes two arguments, a specification of the input movie, and a specification of the output document. The input specification is responsible for providing the input stream from which to read the SWF byte stream, the output RVML document is written to either a stream of a SAX ContentHandler provided by the output specification, and the results of the operation is returned as a boolean to the user.

5.2.1 The SWF Input Document Specification

The SWF input document parameters are defined using the com.kinesis.SWFInputType interface. This interface inherits from the descriptive com.kinesis.SWFInputSpecificationType and provides the following methods:

public String getDocumentIdentifier()
Return a string used to identify the document in output log messages
public Reader getInputStream()
Provides an input stream from which the binary SWF is read
public boolean closeStreamAfterUse()
Returns true if the input stream is to be closed automatically by KineticFusion after use.

It is the responsibility of the user to provide an implementation of this interface based on the requirements of the application however we provide a demonstration class io.SWFFileInput in the examples folder for reading SWF movies from external files:

/**
 * SWFInputType implementation for simple SWF files 
 */
public class SWFFileInput implements SWFInputType {

    private File mySWFFile;
      
    /**
     * Create the input with an SWF file and the output RVML file - the ouptut RVML file
     * is used to calculate the location of the repository for the SWF assets  
     */
    public SWFFileInput( File swfFile)
    {
        if ( swfFile == null ) throw new NullPointerException( "Input file cannot be null");
        if ( !swfFile.isFile()) throw new IllegalArgumentException("Specified file is not an XML file");
        mySWFFile = swfFile;
    }

    
    /**
     * Return an identifier for this document that is used in reporting errors and
     * other status messages 
     * @return
     */
    public String getDocumentIdentifier()
    {
        return mySWFFile.getAbsolutePath();
    }
    
 
    /**
     * Returns an input stream from which to read the SWF movie
     * @return an input stream from which to read the SWF movie
     * @throws IOException if a error occured when trying to create the input stream
     */
   public InputStream getInputStream() throws IOException
    {
        return new BufferedInputStream( new FileInputStream ( mySWFFile));
    }

   /**
    * Set whether  the application should close the stream after reading the SWF document. 
    * If false, it is up to the creator of the InputStream to ensure the reader is closed 
    * appropriately. If a single InputStream is capable of supplying multiple SWF documents 
    * this should be set to false.
    * @return true if the InputStream should be closed automatically by the application after procesing
    */
   public boolean closeStreamAfterUse()
    {
         return true;
    }

5.2.2 The RVML Output Document Specification

There are two possible interfaces that can be implemented to define the RVML output: com.kinesis.RVMLStreamOutputType and com.kinesis.RVMLSAXOutputType interface. Both of these inherit from the base interface com.kinesis.RVMLOutputSpecificationType that provides the following methods:

public String getDocumentIdentifier()
Return a string used to identify the document if output-specific errors occur
public RepositoryParametersType getRepositoryParameters()
Get the specification of the location in which to store resources.

5.2.2.1 Outputting RVML to a Text Stream

The com.kinesis.RVMLStreamOutputType is used to output RVML as UTF-8 text to a specified stream. The interface provides the following methods:

public OutputStream getOutputStream()
Returns the output stream to which the RVML is written
public boolean closeStreamAfterUse()
Returns true if the output stream is to be closed by KineticFusion once the RVML is written

It is the responsibility of the user to provide an implementation of this interface based on the requirements of the application however we provide a demonstration class io.RVMLFileInput in the examples folder for reading SWF movies from external files:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import resources.FileRepositoryParameters;

import com.kinesis.RVMLStreamOutputType;
import com.kinesis.io.resources.RepositoryParametersType;

/**
 * RVMLOutputType implementation for writing RVML documents 
 */
public class RVMLFileOutput implements RVMLStreamOutputType {


    private File myRVMLFile;
    private File myRepositoryRoot;
    
    private RepositoryParametersType myRepositoryParameters;
 
    /**
     * Write the RVML output to a file and use a default fileRepository
     * @param rvmlFile
     */
    public RVMLFileOutput( File rvmlFile)
    {
        initialiseFile( rvmlFile );
         myRepositoryParameters = new FileRepositoryParameters( rvmlFile);
    }
    
    /**
     * Write the RVML output to a file and use the specified RepositoryParametersType for
     * storing all SWF resources
     * @param rvmlFile
     * @param repParameters
     */
    public RVMLFileOutput( File rvmlFile, RepositoryParametersType repParameters)
    {
        initialiseFile( rvmlFile );
        myRepositoryParameters = repParameters;
    }

    
    /**
     * @param rvmlFile
     */
    private void initialiseFile(File rvmlFile)
    {
        if ( rvmlFile == null ) throw new NullPointerException( "Output file cannot be null");
        if ( rvmlFile.isDirectory()) throw new IllegalArgumentException("Specified output is a directory");
        if (rvmlFile.exists() &&  ! rvmlFile.canWrite()) throw new IllegalArgumentException("Cannot write specified location");
        myRVMLFile = rvmlFile;
    }

    /**
     * Returns an output stream in which the RVML document movie can be written
     * @return
     * @throws IOException if a error occured when trying to create the output stream
     */
    public OutputStream getOutputStream() throws IOException
    {
        return new FileOutputStream( myRVMLFile);        
    }
    
    /**
     * Should the application close the output stream  afterwriting the RVML document. If false, it
     * is up to the creator of the output stream  to ensure the stream is closed appropriately.
     * @return
     */
    public boolean closeStreamAfterUse()
    {
        return true;
    }

    
    /**
     * Returns the root repository  folder for the document. 
     * @return
     */
    public File getRepositoryRoot()
    {
        return myRepositoryRoot;
    }
    
    /**
     * Return an identifier for this document that is used in reporting errors and
     * other status messages. Generally this is the short file name of the  SWF document that is being
     * processed. It is combined with the RepositoryRoot File to give a unique location for storing resources. 
     * @return Identifier for the document
     */
    public String getDocumentName()
    {
        return myRVMLFile.getName();
    }
        

    /**
     * Return an identifier for this document that is used in reporting errors and
     * other status messages 
     * @return
     */
    public String getDocumentIdentifier()
    {
        return myRVMLFile.getAbsolutePath();
    }

    
    /**
     * Return an object defining the attributes for storing external resources to be referenced inside the
     * RVML document
     * @return The parameters for the resource repository
     */
    public RepositoryParametersType getRepositoryParameters()
    {
        return myRepositoryParameters;
    }
}

This example uses a FileRepositoryParameters object for storing all resources to a folder on the local File System. This emulates the KineticFusion desktop application. It also allows users to override this behavior by specifying their own RepositoryParametersType that overrides the storage behavior of resources.

5.2.2.2 Outputting RVML to a SAX Handler

Often times it is sufficent to process the RVML in memory using a DOM model or pass-through SAX event handler. In this case there is no need to externalize the RVML. The com.kinesis.RVMLSAXOutputType is used to output RVML as SAX events to a specified SAX ContentHandler. The interface provides the following methods:

public ContentHandler getContentHandler()
Returns the content handler that will receive all the RVML document SAX events

Again, we provide a demonstration implementation class io.RVMLSAXOutput that redirects output to a ContentHandler:

import java.io.File;

import org.xml.sax.ContentHandler;

import resources.FileRepositoryParameters;

import com.kinesis.RVMLSAXOutputType;
import com.kinesis.io.resources.RepositoryParametersType;

public class RVMLSAXOutput implements RVMLSAXOutputType {

    private String myID;

    private ContentHandler myHandler;
    
    private RepositoryParametersType myParameters;

    /**
     * Create with a file used to store external resources and the content handler to receive 
     * RVML SAX Events
     * @param myRVMLFile
     * @param handler
     */
    public RVMLSAXOutput(File myRVMLFile, ContentHandler handler)
    {
        myID =  myRVMLFile.getAbsolutePath();
        myHandler = handler;
        myParameters = new FileRepositoryParameters( myRVMLFile);
    }

    /**
     * Create with an explicit RepositoryParameters type  used to store external resources and 
     * the content handler to receive RVML SAX Events
     * @param id Used for identifying th document in output messages
     * @param repParameters 
     * @param handler
     */
    public RVMLSAXOutput(String id, RepositoryParametersType repParameters, ContentHandler handler)
    {
        myID =  id;
        myHandler = handler;
        myParameters = repParameters;
    }

    /
    public String getDocumentIdentifier()
    {
        return myID;
    }

    public ContentHandler getContentHandler()
    {
        return myHandler;
    }

    public RepositoryParametersType getRepositoryParameters()
    {
        return myParameters;
    }

}

5.2.2.3 Defining Repository Parameters

When decompiling from SWF to RVML, KineticFusion needs to be able to externalize any resources that are stored in the SWF. The com.kinesis.io.resources.RepositoryParametersType interface defines the storage policy for the resources and provides the following methods:

public boolean outputToFileSystem()
When true, all resources are output to the FileSystem, otherwise the user-supplied UserResourceManagerType is used.
public File getRepositoryRoot()
When storing to the file system this defines an absolute path for storing resources that is used if the configuration option kinesis.repository.isRelative is set to false.
public String getDocumentName()
The short name for the document used to define the unique location for resources from this SWF
public UserResourceManagerType getResourceManager()
Get the user-defined storage manager for storing and retrieving resources.

Two examples classes, resources.FileRepositoryParameters and resources.UserRePositoryParameters are provided as exmaple implementations of the RepositoryParametersType interface.

5.2.2.4 Defining Your Own Resource Repository

The KineticFusion Server adds an additional RVML repository type of "Server". Users can create their own stream handlers for storing and reading resources. This is accompished by implementing the interface com.kinesis.io.resources.UserResourceManagerType. Instances of this interface can be provided when decompiling SWF movies and when compiling RVML documents. The UserResourceManagerType is provided by the RepositoryParametersType which is, in turn provided by both the RVMLInputSpecificationType and the RVMLOutputSpecificationType. When converting from SWF to RVML, only a single UserResourceManagerType is used. The resources are stored in streams provided by com.kinesis.io.resources.OutputResourceType output objects defined by the user. The generated RVML references the UserResourceManagerType by setting the Repository element's repositoryType attribute to "Server" and setting the repositoryName attribute to the ID supplied by the UserResourceManagerType.

When compiling RVML to SWF, the ID of the repository is used to retrieve a UserResourceManagerType from the RVMLInputSpecificationType and then all resources are read from this instance as com.kinesis.io.resources.InputResourceType objects.

The UserResourceManagerType interface defines the following methods:

public String getRepositoryID()
Get a unique ID for this repository
public OutputResourceType getFontOutputResource(FontIdentifierType fontID, String resourceName)
Get a stream to write the font specified by the FontIdentifier.
public InputResourceType getFontInputResource(FontIdentifierType fontID, String resourceName)
Get a Resource to read the font specified by the FontIdentifier.
public OutputResourceType getImageOutputResource(String resourceName)
Get the output resource for writing the image identified by specified resource name
public InputResourceType getImageInputResource(String resourceName)
Get the input resource for reading the image identified by specified resource name
public OutputResourceType getSoundOutputResource(String resourceName)
Get the output resource for writing the sound identified by specified resource name
public InputResourceType getSoundInputResource(String resourceName)
Get the input resource for reading the sound identified by specified resource name
public OutputResourceType getVideoOutputResource(String resourceName)
Get the output resource for writing the video identified by specified resource name
public InputResourceType getVideoInputResource(String resourceName)
Get the input resource for reading the video identified by specified resource name
public File getBaseClassPath()
Returns the root path in which to store all AS2.0 classes.

The InputResourceType and OutputResourceType interfaces are used to encapsulate resources and their stream contents. The InputResourceType interface defines the following methods:

public boolean isChangedExternally(long sinceTime)
Return true if the resource contents have been changed externally since the specified time
public boolean resourceExists()
Return true if the resource exists
public String getResourceName()
Return the name for the resource
public InputStream getInputStream()
Return an input stream for the resource. The stream is closed automatically after use.

The OutputResourceType interface defines the following methods:

public boolean isChangedExternally(long sinceTime)
Return true if the resource contents have been changed externally since the specified time
public boolean resourceExists()
Return true if the resource exists
public String getResourceName()
Return the name for the resource
public InputStream getOutputStream()
Return an output stream for the resource. The stream is closed automatically after use.

There is a sample implementation of a volatile ByteResource class (located in resources.ByteArrayResourceManager) that implements both of these interfaces and can be used for caching resources in memory:

    /**
     * Class used to implement the storing of resources as byte arrays. The byte array is created only
     * by populating the output stream. Once the resource has been written to the output stream
     * it can be provided as an input stream.While the resource is being written the original contents will
     * always be available for reading. 
     */
    final class ByteResource implements InputResourceType, OutputResourceType
    {
        private byte[] myContents;
        
        private String myName;
        
        private long lastModified = 0;
        /**
         * @param stream
         * @param name
         */
        protected ByteResource(String name)
        {
            myName = name;
        }

        /**
         * Returns a stream that will updates this object when it is closed 
         * @return
         * @throws IOException
         */
         synchronized public OutputStream getOutputStream() throws IOException
        {
            return new ResourceByteStream( this);
        }
        
         /**
          * When the OutputStream is closed by the client the contents of the stream are copied
          * into the resource using this method
          * @param contents
          */
         synchronized protected void setContents( byte[] contents)
         {
             if (myContents != null)
                 throw new IllegalStateException("Contents of resource are already defined");
             myContents = contents;
             lastModified = System.currentTimeMillis();
         }
         
        /**
         * Return an input stream - if the contents have not yet been read the contents 
         * of the stream will be zero. 
          * @return
         * @throws IOException
         */
        synchronized public InputStream getInputStream() throws IOException
        {
            byte[] contents = ( myContents == null)? new byte[0] : myContents;            
            return new ByteArrayInputStream( contents);
        }

        /**
         *  The contents are deemed to have changed if the resource has been written since the specified time
         * @param sinceTime
         * @return
         */
        synchronized public boolean isChangedExternally(long sinceTime)
        {
            return lastModified > sinceTime;
        }

        /**
         * Resource exists only if it has been populated by an output stream
         * @return
         */
        synchronized public boolean resourceExists()
        {
            return myContents != null;
        }

        /**
         * Returns the redefined name of the resource 
         * @return
         */
        public String getResourceName()
        {
             return myName;
        }
    }

This class assumes that resources are output into a class instance before they are read. In order to process the incoming bytes and update the ByteResource instance with the contents of the resource a sub-class of ByteArrayOutputStream is used:

   /**
     * Byte stream used to load the contents of a resource. Once the stream is closed then contents 
     * are moved into the resource
     */
    static private final class ResourceByteStream extends ByteArrayOutputStream{

            private ByteResource myResource;
            
            public ResourceByteStream( ByteResource contents)
            {
                myResource = contents;
            }
            
            public void close() throws IOException
            {
                super.close();
                myResource.setContents(toByteArray());
            }
     }