Skip to content

Home

Introduction

Development

IDEs

Services

API Explorer

Releases

Release notes

Martini Runtime

Martini Desktop

Martini Online

Feature requests

Bug reports

Directory watcher endpoint

The directory watcher endpoint enables developers to write applications that react to changes occurring in a monitored directory in the server's local file system. This may be applicable to scenarios where other components write files which Martini needs to process.

Unsupported by Martini Online

You cannot create nor edit a directory watcher endpoint in Martini Online.

Shared vs. local file systems

The directory watcher depends on the underlying file system to be notified of changes to the file system. Although this works well for local file systems on Windows, macOS and Linux, it may not work on remote or shared file systems. In these scenarios, it's recommended to use the scheduler endpoint and watch for file system changes manually.

Properties

General configuration

Property Default Description
Name (required) The name of the endpoint.
Service (required) The service to execute when the endpoint is triggered.
Run As Anonymous The user to run the service in behalf of. This is logged to Tracker.
Document Type <Name of endpoint type> The document type to be used when adding documents to Tracker as this endpoint is triggered.
Auto Start true Whether or not to automatically start the endpoint upon package startup.
Log To Tracker false Flag determining whether executions should be logged to Tracker.

Directory watcher-specific configuration

Property Default Description
Directory (required) The directory to be monitored for changes.
Recursive false Flag determining whether subdirectories are also monitored for changes.
Events (required) The type of events this endpoint should monitor. Options are: 'Addition', 'Modification', 'Deletion'.

Service

When the endpoint is triggered, the following variables are exposed to the configured service:

General parameters

Name Type Description
internalId java.lang.String The Tracker document internal ID. If the endpoint was configured to not track, this value will be null.
$trackerId java.lang.String An alias for the internalId property.
martiniPackage MartiniPackage The Martini package that contains the endpoint.
parameters java.util.Map A map containing all the endpoint specific parameters.
properties java.util.Map A map containing containing all the properties associated with the endpoint.

Directory watcher-specific parameters

Name Type Description
fileAction io.toro.martini.event.FileEvent.Action The action that triggered this endpoint. Expected values are: ADDED, MODIFIED, or DELETED.
action java.lang.String String version of the fileAction.
file java.nio.file.Path The file whose change triggered the endpoint.
filename java.lang.String The absolute path of the file.
inputStream java.io.InputStream An open InputStream, pointing to the file. The stream is automatically closed after the service executes.
reader java.io.Reader An open Reader, pointing to the file. The reader is automatically closed after the service executes.
multipartFile org.springframework.web.multipart.MultipartFile A multipart file pointing to the file.
bytes byte[] Contains all file data. This variable is only created if your method has a parameter that matches the name (therefore scripts will never have this variable since they don't declare variables).
content java.lang.String Contains all file data. This variable is only created if your method has a parameter that matches the name (therefore scripts will never have this variable since they don't declare variables).

Examples

Gloop as service

Consider this application using Gloop that sends the file that triggered the endpoint to an remote FTP server:

File-sending service

(1) Takes the credentials of the FTP server from the package properties. (5) When the service is executed, this fetches the remote representation of the file matching filename from the input (provided by the endpoint):

File-sending service's inputs

(6) The value of bytes is then written to the remote representation of the file resolved by the previous line.

Want more examples?

The distribution ships with a Martini package called examples, which contains services (including the above example) demonstrating more use cases.

Groovy script as service

Consider this Groovy script that simply prints the available variables in the context:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[
    'properties',
    'fileAction',
    'action',
    'file',
    'filename',
    'inputStream',
    'reader',
    'multipartFile',
    'internalId' ].each {

    println "$it\t : " + this[it]
}

When the endpoint is triggered - in this case, the file naenae.dance being added to /tmp/watch-me/ - the console will show logs similar to the following:

1
2
3
4
5
6
7
8
9
properties   : [runAs:, documentType:Directory Watcher, track:false, directory:/tmp/watch-me, events:onAdd,onDelete,onModify, recursive:false]
fileAction  : ADDED
action   : ADDED
file     : /tmp/watch-me/naenae.dance
filename     : /tmp/watch-me/naenae.dance
inputStream  : java.io.FileInputStream@4b5fa778
reader  : java.io.InputStreamReader@5cffa221
multipartFile    : io.toro.martini.core.util.ESBMockMultipartFile@30b7ae2b
internalId   : null

In combination with the Groovy extension modules, we can write scripts that address more advance use cases, such as:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// get a groovy.sql.SQL object that points to the connection called 'customers'
int count = 0
'customers'.sql() {  sql ->
    def query = 'insert into my_customers ( id, first_name,  last_name, address1, address2, city, state, postcode ) values ( ?, ?, ?, ?, ?, ?, ?, ? )'
    sql.withBatch(1000, query) { ps ->
        // iterate over the csv file, skipping the first row
        inputStream.eachRecord(1) { it ->
            ps.addBatch(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7])
            count++
            if (count % 1000 == 0) println "processed ${count}"
        }
    }
}

This script iterates a comma-separated value (CSV) file and batch inserts the data in the database customers.

Groovy method as service

By using a Groovy class method as the service configured to the endpoint, we get access to additional variables content and bytes. Consider the following script:

1
2
3
4
5
6
7
class Reporter {

    def sendFileAsEmailBody( String content ) {
        // smtps format is smtp[s]://<login>:<password>@<smtp-server>[:port]/<Subject>
        'smtps://myEmail:password@server/Daily Report'.send( [to: 'daily-reports@work.com'], content );
    }
}
This is invoked with the content of the file and sends it as an email body to daily-reports@work.com with subject Daily Report.

Use inputStream or file if reading large files

The use of content and bytes is suited only for cases where the expected size of the file is small. Otherwise, reading via the inputStream, or accessing the file directly is recommended.

In combination with the Groovy extension modules, scripts can be written that address more advanced use cases, such as:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// get a groovy.sql.SQL object that points to the connection pool called 'customers'
int count = 0
'customers'.sql() {  sql ->
    def query = 'insert into my_customers ( id, first_name,  last_name, address1, address2, city, state, postcode ) values ( ?, ?, ?, ?, ?, ?, ?, ? )'
    sql.withBatch(1000, query) { ps ->
        // iterate over the csv file, skipping the first row
        file.eachRecord(1) { it ->
            ps.addBatch(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7])
            count++
            if (count % 1000 == 0) println "processed ${count}"
        }
    }
}

This script iterates a comma-separated value (CSV) file and batch inserts the data in the database customers.