The Publish-To-Filesystem(P2FS) service writes published content to the file system so that another webserver (e.g. Apache) can serve this content without further intervention by CMS. This makes it possible to build highly scalable live servers that are not bound by factors such as database load.

Whenever content in CMS is published or unpublished, a corresponding task is scheduled with P2FS to update the file system appropriately. The {@link com.arsdigita.cms.publishToFile.QueueManager queue manager} periodically checks this queue and processes all entries in it. An item is (re)published by requesting its live version from the editorial server through HTTP. The returned content is the scanned for links to other items (by looking for `oid` attributes in certain HTML tags). The links are rewritten to properly work on the targetted live server (see the `destination` parameter below). In addition to the main HTML file for an item, P2FS will also write all associated local assets of the item to the file system.

P2FS deployments will have some kind of shared storage between the application server running CMS/P2FS and the web server serving published content. One typical example is for P2FS to publish to a directory that is NFS-shared between the application server and the web server. Then, when the application server writes new content the to file system, it will show up under the web server. Another possible configuration is for the application server and web server to be located on the same physical machine. Then, the web server could simply link to the directory to which P2FS is writing content.

## Configuration

To use P2FS, you need to add a `com.arsdigita.cms.publishToFile.Initializer` entry to your `enterprise.init`. For example:

    init com.arsdigita.cms.publishToFile.Initializer {
        destination = {
          { "com.arsdigita.cms.ContentItem",
            "/var/p2fs",
            false,
            "/var/p2fs" },
          { "com.arsdigita.cms.Template",
            "/packages/content-section/templates",
            false,
            "/packages/content-section/templates" }
        };
        publishListener = "com.arsdigita.cms.publishToFile.PublishToFile";
        startupDelay = 30;
        pollDelay = 5;
        retryDelay = 120;
        blockSize = 40;
        maximumFailCount = 10;
        blockSelectMethod = "GroupByParent";
    }

The first parameter, `destination`, is a list of publish destinations for content types. Each element in the `desitnation` list is a four-element list in the format:
`'{ "content type", "root directory", "shared storage", "url stub" }'.`
Content type is the object type of the content type. Root directory must be a path to a writable directory, relative to the webapp root. Shared storage must be true if the root directory is shared NFS storage, false otherwise. URL stub must be the path component of the URL from which the live server will serve from this directory.

The `publishListener` must name a class that implements {@link com.arsdigita.cms.publishToFile.PublishToFileListener PublishToFileListener}. The listener is called whenever changes to the file system need to be made. It is strongly recommended that the listener in fact subclasses {@link com.arsdigita.cms.publishToFile.PublishToFile PublishToFile}, since most of the basic P2FS functionality is provided by this class.

The remaining parameters control how the queue of items that need to be written to the file system is processed. Queue processing will start `startupDelay` seconds after the server start, and the queue will be polled for neew entries every `pollDelay` seconds. If `startupDelay` is set to 0, queue processing is completely disabled.

The queue manager will process `blockSize` queue entries within one database transaction. Setting this parameter to a higher value can speed up the overall P2FS performance, but will also consume more memory during processing.

If processing a queue entry fails for any reason, the processing is retried after `retryDelay` seconds. Entries that could not successfully be processed after `maximumFailCount` will remain in the queue, but will be ignored. Processing an entry can fail for a variety of reasons, for example because the target file system is full, or because the content could not be retrieved from the editorial server through an HTTP request. The server log will contain more detailed information about all procesing failures.

The `blockSelectMethod` must always be set to `GroupByParent`.

### Multi-JVM Deployment

If CMS is run on multiple JVMS (servlet containers), the queue processor must be disabled on all but one of these servers by setting `startupDelay` to 0. It is not possible to have several servers process the queue at the same time.

## Enabling Templates for P2FS

P2FS scans all content for the HTML tags `<a>`, `<img>`, and <link>. Any such tags that contain an `oid` attribute will be rewritten by P2FS and adjusted to the configured publishing destination. The `oid` attribute must contain the ID of the target content item. For example, the fragment

                  <a oid="425">Another Item</a>

will be rewritten by P2FS so that the HTML link points to the correct file on the destination live server for the content item with ID 425.

P2FS uses the `ContentType` HTTP header it received when it requested the live item to determine the file extension of the main file written for an item. Currently, the following content types are supported:

Content Type | File Extension
--- | ---
`text/html` | `.html`
`text/plain` | `.txt`
`application/php` | `.php`
`application/xml` | `.xml`
other | `.html`