Set of disabled paths
     */
    public Long getDisabledPathsId()
    {
        return disabledPathsId;
    }
    /**
     * Helper method to check that a path is correct for this application instance
     * 
     * @param path              the path in format /app-key/x/y/z
     * @throws AuditModelException      if the path is invalid
     * 
     * @see #AUDIT_PATH_PATTERN
     */
    public void checkPath(String path)
    {
        checkPathFormat(path);
        if (path == null || path.length() == 0)
        {
            generateException(path, "Empty or null audit path");
        }
        else if (!AUDIT_PATH_PATTERN.matcher(path).matches())
        {
            generateException(
                    path,
                    "An audit must match regular expression: " + AUDIT_PATH_PATTERN);
        }
        else if (path.indexOf(applicationKey, 0) != 1)
        {
            generateException(
                    path,
                    "An audit path's first element must be the application's key i.e. '" + applicationKey + "'.");
        }
    }
    
    /**
     * Helper method to check that a path is correct for this application instance
     * 
     * @param path              the path in format /app-key/x/y/z
     * @throws AuditModelException      if the path is invalid
     * 
     * @see #AUDIT_PATH_PATTERN
     */
    public static void checkPathFormat(String path)
    {
        if (path == null || path.length() == 0)
        {
            throw new AuditModelException("Empty or null audit path: " + path);
        }
        else if (!AUDIT_PATH_PATTERN.matcher(path).matches())
        {
            throw new AuditModelException(
                        "Audit path '" + path + "' does not match regular expression: " + AUDIT_PATH_PATTERN);
        }
    }
    
    /**
     * Compile a path or part of a path into a single string which always starts with the
     * {@link #AUDIT_PATH_SEPARATOR}.  This can be a relative path so need not always start with
     * the application root key.
     * 
     * If the path separator is present at the beginning of a path component, then it is not added,
     * so "/a", "b", "/c" becomes "/a/b/c" allowing path to be appended
     * to other paths.
     * 
     * The final result is checked against a {@link #AUDIT_PATH_PATTERN regular expression} to ensure
     * it is valid.
     * 
     * @param pathComponents      the elements of the path e.g. "a", "b", "c".
     * @return                  Returns the compiled path e.g "/a/b/c".
     */
    public static String buildPath(String ... pathComponents)
    {
        StringBuilder sb = new StringBuilder(pathComponents.length * 10);
        for (String pathComponent : pathComponents)
        {
            if (!pathComponent.startsWith(AUDIT_PATH_SEPARATOR))
            {
                sb.append(AUDIT_PATH_SEPARATOR);
            }
            sb.append(pathComponent);
        }
        String path = sb.toString();
        // Check the path format
        if (!AUDIT_PATH_PATTERN.matcher(path).matches())
        {
            StringBuffer msg = new StringBuffer();
            msg.append("The audit path is invalid and must be matched by regular expression: ").append(AUDIT_PATH_PATTERN).append("\n")
               .append("   Path elements: ");
            for (String pathComponent : pathComponents)
            {
                msg.append(pathComponent).append(", ");
            }
            msg.append("\n")
               .append("   Result:        ").append(path);
            throw new AuditModelException(msg.toString());
        }
        // Done
        return path;
    }
    
    /**
     * @param path              the audit path for form /abc/def
     * @return                  the root key of form abc
     */
    public static String getRootKey(String path)
    {
        if (!path.startsWith(AUDIT_PATH_SEPARATOR))
        {
            throw new AuditModelException(
                    "The path must start with the path separator '" + AUDIT_PATH_SEPARATOR + "'");
        }
        String rootPath;
        int index = path.indexOf(AUDIT_PATH_SEPARATOR, 1);
        if (index > 0)
        {
            rootPath = path.substring(1, index);
        }
        else
        {
            rootPath = path.substring(1);
        }
        // Done
        return rootPath;
    }
    
    /**
     * Utility class carrying information around a {@link DataExtractor}.
     * 
     * @author Derek Hulley
     * @since 3.4
     */
    public static class DataExtractorDefinition
    {
        private final String dataTrigger;
        private final String dataSource;
        private final String dataTarget;
        private final DataExtractor dataExtractor;
        /**
         * @param dataTrigger           the data path that must exist for this extractor to be triggered
         * @param dataSource            the path to get data from
         * @param dataTarget            the path to write data to
         * @param dataExtractor         the implementation to use
         */
        public DataExtractorDefinition(String dataTrigger, String dataSource, String dataTarget, DataExtractor dataExtractor)
        {
            this.dataTrigger = dataTrigger;
            this.dataSource = dataSource;
            this.dataTarget = dataTarget;
            this.dataExtractor = dataExtractor;
        }
        /**
         * The data path that must exist for the extractor to be triggered.
         */
        public String getDataTrigger()
        {
            return dataTrigger;
        }
        public String getDataSource()
        {
            return dataSource;
        }
        public String getDataTarget()
        {
            return dataTarget;
        }
        public DataExtractor getDataExtractor()
        {
            return dataExtractor;
        }
    }
    
    /**
     * Get all data extractors applicable to this application.
     * 
     * @return                  Returns all data extractors contained in the application
     */
    public List