From 33092f68b1dfc3b9dae653b8b0b95cbab5381d5c Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 11 Oct 2011 16:34:58 +0000
Subject: [PATCH] Fix OPENDJ-308: Implement access log filtering and configurable message format

---
 opends/resource/schema/02-config.ldif                                                          |   34 +
 opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java                         |   23 
 opends/src/admin/defn/org/opends/server/admin/std/AccessLogFilteringCriteriaConfiguration.xml  |   98 ++++
 opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java                        |  885 ++++++++++++++++++++++++++----------
 opends/src/admin/defn/org/opends/server/admin/std/FileBasedAccessLogPublisherConfiguration.xml |   46 +
 opends/src/admin/defn/org/opends/server/admin/std/FileBasedAuditLogPublisherConfiguration.xml  |  288 ++++++++++++
 opends/src/admin/messages/FileBasedAuditLogPublisherCfgDefn.properties                         |   26 +
 opends/src/admin/messages/FileBasedAccessLogPublisherCfgDefn.properties                        |    7 
 opends/resource/config/config.ldif                                                             |    2 
 opends/src/admin/messages/AccessLogFilteringCriteriaCfgDefn.properties                         |   17 
 10 files changed, 1,165 insertions(+), 261 deletions(-)

diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 6a52c56..836689e 100644
--- a/opends/resource/config/config.ldif
+++ b/opends/resource/config/config.ldif
@@ -706,7 +706,7 @@
 objectClass: top
 objectClass: ds-cfg-log-publisher
 objectClass: ds-cfg-access-log-publisher
-objectClass: ds-cfg-file-based-access-log-publisher
+objectClass: ds-cfg-file-based-audit-log-publisher
 cn: File-Based Audit Logger
 ds-cfg-java-class: org.opends.server.loggers.TextAuditLogPublisher
 ds-cfg-enabled: false
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index 06b6ad4..9887e52 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -2668,6 +2668,14 @@
   NAME 'ds-cfg-rename-inbound-attributes'
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
   X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.30
+  NAME 'ds-cfg-filtering-policy'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.31
+  NAME 'ds-cfg-log-record-type'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  X-ORIGIN 'OpenDJ Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler'
   SUP top
@@ -2932,7 +2940,8 @@
         ds-cfg-buffer-size $
         ds-cfg-auto-flush $
         ds-cfg-append $
-        ds-cfg-queue-size )
+        ds-cfg-queue-size $
+        ds-cfg-filtering-policy )
   X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.26
   NAME 'ds-cfg-file-based-debug-log-publisher'
@@ -4442,3 +4451,26 @@
   MAY ( ds-cfg-remove-inbound-attributes $
         ds-cfg-rename-inbound-attributes )
   X-ORIGIN 'OpenDJ Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.6
+  NAME 'ds-cfg-file-based-audit-log-publisher'
+  SUP ds-cfg-access-log-publisher
+  STRUCTURAL
+  MUST ( ds-cfg-log-file $
+         ds-cfg-asynchronous $
+         ds-cfg-log-file-permissions )
+  MAY ( ds-cfg-rotation-policy $
+        ds-cfg-rotation-action $
+        ds-cfg-retention-policy $
+        ds-cfg-time-interval $
+        ds-cfg-buffer-size $
+        ds-cfg-auto-flush $
+        ds-cfg-append $
+        ds-cfg-queue-size )
+  X-ORIGIN 'OpenDJ Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.7
+  NAME 'ds-cfg-access-log-filtering-criteria'
+  SUP top
+  STRUCTURAL
+  MUST ( cn )
+  MAY ( ds-cfg-log-record-type )
+  X-ORIGIN 'OpenDJ Directory Server' )
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/AccessLogFilteringCriteriaConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/AccessLogFilteringCriteriaConfiguration.xml
new file mode 100644
index 0000000..ede1982
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/AccessLogFilteringCriteriaConfiguration.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ! CDDL HEADER START
+  !
+  ! The contents of this file are subject to the terms of the
+  ! Common Development and Distribution License, Version 1.0 only
+  ! (the "License").  You may not use this file except in compliance
+  ! with the License.
+  !
+  ! You can obtain a copy of the license at
+  ! trunk/opends/resource/legal-notices/OpenDS.LICENSE
+  ! or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+  ! See the License for the specific language governing permissions
+  ! and limitations under the License.
+  !
+  ! When distributing Covered Code, include this CDDL HEADER in each
+  ! file and include the License file at
+  ! trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+  ! add the following below this CDDL HEADER, with the fields enclosed
+  ! by brackets "[]" replaced with your own identifying information:
+  !      Portions Copyright [yyyy] [name of copyright owner]
+  !
+  ! CDDL HEADER END
+  !
+  !
+  !      Copyright 2011 ForgeRock AS.
+  ! -->
+<adm:managed-object name="access-log-filtering-criteria"
+  plural-name="access-log-filtering-criteria"
+  package="org.opends.server.admin.std"
+  xmlns:adm="http://www.opends.org/admin"
+  xmlns:ldap="http://www.opends.org/admin-ldap">
+  <adm:synopsis>
+    A set of rules which together determine whether a log record should be
+    logged or not.
+  </adm:synopsis>
+  <adm:tag name="core-server"/>
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:name>ds-cfg-access-log-filtering-criteria</ldap:name>
+      <ldap:superior>top</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+  <adm:property name="log-record-type" multi-valued="true">
+    <adm:synopsis>
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:alias>
+        <adm:synopsis>All records</adm:synopsis>
+      </adm:alias>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:enumeration>
+        <adm:value name="abandon">
+          <adm:synopsis>Abandon operations</adm:synopsis>
+        </adm:value>
+        <adm:value name="add">
+          <adm:synopsis>Add operations</adm:synopsis>
+        </adm:value>
+        <adm:value name="bind">
+          <adm:synopsis>Bind operations</adm:synopsis>
+        </adm:value>
+        <adm:value name="compare">
+          <adm:synopsis>Compare operations</adm:synopsis>
+        </adm:value>
+        <adm:value name="delete">
+          <adm:synopsis>Delete operations</adm:synopsis>
+        </adm:value>
+        <adm:value name="extended">
+          <adm:synopsis>Extended operations</adm:synopsis>
+        </adm:value>
+        <adm:value name="modify">
+          <adm:synopsis>Modify operations</adm:synopsis>
+        </adm:value>
+        <adm:value name="rename">
+          <adm:synopsis>Rename operations</adm:synopsis>
+        </adm:value>
+        <adm:value name="search">
+          <adm:synopsis>Search operations</adm:synopsis>
+        </adm:value>
+        <adm:value name="unbind">
+          <adm:synopsis>Unbind operations</adm:synopsis>
+        </adm:value>
+        <adm:value name="connect">
+          <adm:synopsis>Client connections</adm:synopsis>
+        </adm:value>
+        <adm:value name="disconnect">
+          <adm:synopsis>Client disconnections</adm:synopsis>
+        </adm:value>
+      </adm:enumeration>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-log-record-type</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+</adm:managed-object>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAccessLogPublisherConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAccessLogPublisherConfiguration.xml
index f18115d..3249731 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAccessLogPublisherConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAccessLogPublisherConfiguration.xml
@@ -24,6 +24,7 @@
   !
   !
   !      Copyright 2007-2009 Sun Microsystems, Inc.
+  !      Portions copyright 2011 ForgeRock AS.
   ! -->
 <adm:managed-object name="file-based-access-log-publisher"
   plural-name="file-based-access-log-publishers"
@@ -40,6 +41,15 @@
       <ldap:superior>ds-cfg-access-log-publisher</ldap:superior>
     </ldap:object-class>
   </adm:profile>
+  <adm:relation name="access-log-filtering-criteria">
+    <adm:synopsis>
+      The set of criteria which will be used to filter log records.
+    </adm:synopsis>
+    <adm:one-to-many/>
+    <adm:profile name="ldap">
+      <ldap:rdn-sequence>cn=Filtering Criteria</ldap:rdn-sequence>
+    </adm:profile>
+  </adm:relation>
   <adm:property-override name="java-class" advanced="true">
     <adm:default-behavior>
       <adm:defined>
@@ -285,4 +295,40 @@
       </ldap:attribute>
     </adm:profile>
   </adm:property>
+  <adm:property name="filtering-policy">
+    <adm:synopsis>
+      Specifies how filtering criteria should be applied to log records. 
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>no-filtering</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:enumeration>
+        <adm:value name="no-filtering">
+          <adm:synopsis>
+            No filtering will be performed, and all records will be logged.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="inclusive">
+          <adm:synopsis>
+            Records must match at least one of the filtering criteria in order
+            to be logged.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="exclusive">
+          <adm:synopsis>
+            Records must not match any of the filtering criteria in order to be
+            logged.
+          </adm:synopsis>
+        </adm:value>
+      </adm:enumeration>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-filtering-policy</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
 </adm:managed-object>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAuditLogPublisherConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAuditLogPublisherConfiguration.xml
new file mode 100644
index 0000000..23b82c2
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/FileBasedAuditLogPublisherConfiguration.xml
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ! CDDL HEADER START
+  !
+  ! The contents of this file are subject to the terms of the
+  ! Common Development and Distribution License, Version 1.0 only
+  ! (the "License").  You may not use this file except in compliance
+  ! with the License.
+  !
+  ! You can obtain a copy of the license at
+  ! trunk/opends/resource/legal-notices/OpenDS.LICENSE
+  ! or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+  ! See the License for the specific language governing permissions
+  ! and limitations under the License.
+  !
+  ! When distributing Covered Code, include this CDDL HEADER in each
+  ! file and include the License file at
+  ! trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+  ! add the following below this CDDL HEADER, with the fields enclosed
+  ! by brackets "[]" replaced with your own identifying information:
+  !      Portions Copyright [yyyy] [name of copyright owner]
+  !
+  ! CDDL HEADER END
+  !
+  !
+  !      Copyright 2011 ForgeRock AS.
+  ! -->
+<adm:managed-object name="file-based-audit-log-publisher"
+  plural-name="file-based-audit-log-publishers"
+  package="org.opends.server.admin.std" extends="access-log-publisher"
+  xmlns:adm="http://www.opends.org/admin"
+  xmlns:ldap="http://www.opends.org/admin-ldap">
+  <adm:synopsis>
+    <adm:user-friendly-plural-name />
+    publish access messages to the file system.
+  </adm:synopsis>
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:name>ds-cfg-file-based-audit-log-publisher</ldap:name>
+      <ldap:superior>ds-cfg-access-log-publisher</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+  <adm:property-override name="java-class" advanced="true">
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>
+          org.opends.server.loggers.TextAuditLogPublisher
+        </adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+  </adm:property-override>
+  <adm:property name="asynchronous" mandatory="true" advanced="true">
+    <adm:synopsis>
+      Indicates whether the
+      <adm:user-friendly-name />
+      will publish records asynchronously.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>true</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:boolean />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-asynchronous</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="queue-size" advanced="true">
+    <adm:synopsis>
+      The maximum number of log records that can be stored in the
+      asynchronous queue.
+    </adm:synopsis>
+    <adm:description>
+      Setting the queue size to zero activates parallel log writer
+      implementation which has no queue size limit and as such the
+      parallel log writer should only be used on a very well tuned
+      server configuration to avoid potential out of memory errors.
+    </adm:description>
+    <adm:requires-admin-action>
+      <adm:other>
+        <adm:synopsis>
+          The <adm:user-friendly-name /> must be restarted if this property 
+          is changed and the asynchronous property is set to true.
+        </adm:synopsis>
+      </adm:other>
+    </adm:requires-admin-action>
+        <adm:default-behavior>
+      <adm:defined>
+        <adm:value>5000</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:integer lower-limit="0" />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-queue-size</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="log-file" mandatory="true">
+    <adm:synopsis>
+      The file name to use for the log files generated by the
+      <adm:user-friendly-name />.
+      The path to the file is relative to the server root.
+    </adm:synopsis>
+    <adm:requires-admin-action>
+      <adm:component-restart />
+    </adm:requires-admin-action>
+   <adm:syntax>
+     <adm:string>
+       <adm:pattern>
+        <adm:regex>.*</adm:regex>
+        <adm:usage>FILE</adm:usage>
+          <adm:synopsis>
+            A path to an existing file that is readable by the server.
+          </adm:synopsis>
+        </adm:pattern>
+      </adm:string>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-log-file</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="log-file-permissions" mandatory="true">
+    <adm:synopsis>
+      The UNIX permissions of the log files created by this
+      <adm:user-friendly-name />.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>640</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string>
+        <adm:pattern>
+          <adm:regex>^([0-7][0-7][0-7])$</adm:regex>
+          <adm:usage>MODE</adm:usage>
+          <adm:synopsis>
+            A valid UNIX mode string. The mode string must contain
+            three digits between zero and seven.
+          </adm:synopsis>
+        </adm:pattern>
+      </adm:string>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-log-file-permissions</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="time-interval" advanced="true">
+    <adm:synopsis>
+      Specifies the interval at which to check whether the log files 
+      need to be rotated.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>5s</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:duration base-unit="ms" lower-limit="1" />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-time-interval</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="buffer-size" advanced="true">
+    <adm:synopsis>Specifies the log file buffer size.</adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>64kb</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:size lower-limit="1" />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-buffer-size</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="auto-flush" advanced="true">
+    <adm:synopsis>
+      Specifies whether to flush the writer after every log record.
+    </adm:synopsis>
+    <adm:description>
+      If the asynchronous writes option is used, the writer is
+      flushed after all the log records in the queue are written.
+    </adm:description>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>true</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:boolean />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-auto-flush</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="append">
+    <adm:synopsis>
+      Specifies whether to append to existing log files.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>true</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:boolean />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-append</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="rotation-policy" multi-valued="true">
+    <adm:synopsis>
+      The rotation policy to use for the
+      <adm:user-friendly-name />
+      .
+    </adm:synopsis>
+    <adm:description>
+      When multiple policies are used, rotation will occur if any
+      policy's conditions are met.
+    </adm:description>
+    <adm:default-behavior>
+      <adm:alias>
+        <adm:synopsis>
+          No rotation policy is used and log rotation will not occur.
+        </adm:synopsis>
+      </adm:alias>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:aggregation parent-path="/"
+        relation-name="log-rotation-policy" />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-rotation-policy</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="retention-policy" multi-valued="true">
+    <adm:synopsis>
+      The retention policy to use for the
+      <adm:user-friendly-name />
+      .
+    </adm:synopsis>
+    <adm:description>
+      When multiple policies are used, log files are cleaned when
+      any of the policy's conditions are met.
+    </adm:description>
+    <adm:default-behavior>
+      <adm:alias>
+        <adm:synopsis>
+          No retention policy is used and log files are never cleaned.
+        </adm:synopsis>
+      </adm:alias>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:aggregation parent-path="/"
+        relation-name="log-retention-policy" />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-retention-policy</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+</adm:managed-object>
diff --git a/opends/src/admin/messages/AccessLogFilteringCriteriaCfgDefn.properties b/opends/src/admin/messages/AccessLogFilteringCriteriaCfgDefn.properties
new file mode 100644
index 0000000..00038dc
--- /dev/null
+++ b/opends/src/admin/messages/AccessLogFilteringCriteriaCfgDefn.properties
@@ -0,0 +1,17 @@
+user-friendly-name=Access Log Filtering Criteria
+user-friendly-plural-name=Access Log Filtering Criteria
+synopsis=A set of rules which together determine whether a log record should be logged or not.
+property.log-record-type.synopsis=
+property.log-record-type.default-behavior.alias.synopsis=All records
+property.log-record-type.syntax.enumeration.value.abandon.synopsis=Abandon operations
+property.log-record-type.syntax.enumeration.value.add.synopsis=Add operations
+property.log-record-type.syntax.enumeration.value.bind.synopsis=Bind operations
+property.log-record-type.syntax.enumeration.value.compare.synopsis=Compare operations
+property.log-record-type.syntax.enumeration.value.connect.synopsis=Client connections
+property.log-record-type.syntax.enumeration.value.delete.synopsis=Delete operations
+property.log-record-type.syntax.enumeration.value.disconnect.synopsis=Client disconnections
+property.log-record-type.syntax.enumeration.value.extended.synopsis=Extended operations
+property.log-record-type.syntax.enumeration.value.modify.synopsis=Modify operations
+property.log-record-type.syntax.enumeration.value.rename.synopsis=Rename operations
+property.log-record-type.syntax.enumeration.value.search.synopsis=Search operations
+property.log-record-type.syntax.enumeration.value.unbind.synopsis=Unbind operations
diff --git a/opends/src/admin/messages/FileBasedAccessLogPublisherCfgDefn.properties b/opends/src/admin/messages/FileBasedAccessLogPublisherCfgDefn.properties
index 30cd2fe..b71b4c3 100644
--- a/opends/src/admin/messages/FileBasedAccessLogPublisherCfgDefn.properties
+++ b/opends/src/admin/messages/FileBasedAccessLogPublisherCfgDefn.properties
@@ -7,6 +7,10 @@
 property.auto-flush.description=If the asynchronous writes option is used, the writer is flushed after all the log records in the queue are written.
 property.buffer-size.synopsis=Specifies the log file buffer size.
 property.enabled.synopsis=Indicates whether the File Based Access Log Publisher is enabled for use.
+property.filtering-policy.synopsis=Specifies how filtering criteria should be applied to log records.
+property.filtering-policy.syntax.enumeration.value.exclusive.synopsis=Records must not match any of the filtering criteria in order to be logged.
+property.filtering-policy.syntax.enumeration.value.inclusive.synopsis=Records must match at least one of the filtering criteria in order to be logged.
+property.filtering-policy.syntax.enumeration.value.no-filtering.synopsis=No filtering will be performed, and all records will be logged.
 property.java-class.synopsis=The fully-qualified name of the Java class that provides the File Based Access Log Publisher implementation.
 property.log-file.synopsis=The file name to use for the log files generated by the File Based Access Log Publisher. The path to the file is relative to the server root.
 property.log-file.syntax.string.pattern.synopsis=A path to an existing file that is readable by the server.
@@ -24,3 +28,6 @@
 property.suppress-internal-operations.synopsis=Indicates whether internal operations (for example, operations that are initiated by plugins) should be logged along with the operations that are requested by users.
 property.suppress-synchronization-operations.synopsis=Indicates whether access messages that are generated by synchronization operations should be suppressed.
 property.time-interval.synopsis=Specifies the interval at which to check whether the log files need to be rotated.
+relation.access-log-filtering-criteria.user-friendly-name=Access Log Filtering Criteria
+relation.access-log-filtering-criteria.user-friendly-plural-name=Access Log Filtering Criteria
+relation.access-log-filtering-criteria.synopsis=The set of criteria which will be used to filter log records.
diff --git a/opends/src/admin/messages/FileBasedAuditLogPublisherCfgDefn.properties b/opends/src/admin/messages/FileBasedAuditLogPublisherCfgDefn.properties
new file mode 100644
index 0000000..c684c21
--- /dev/null
+++ b/opends/src/admin/messages/FileBasedAuditLogPublisherCfgDefn.properties
@@ -0,0 +1,26 @@
+user-friendly-name=File Based Audit Log Publisher
+user-friendly-plural-name=File Based Audit Log Publishers
+synopsis=File Based Audit Log Publishers publish access messages to the file system.
+property.append.synopsis=Specifies whether to append to existing log files.
+property.asynchronous.synopsis=Indicates whether the File Based Audit Log Publisher will publish records asynchronously.
+property.auto-flush.synopsis=Specifies whether to flush the writer after every log record.
+property.auto-flush.description=If the asynchronous writes option is used, the writer is flushed after all the log records in the queue are written.
+property.buffer-size.synopsis=Specifies the log file buffer size.
+property.enabled.synopsis=Indicates whether the File Based Audit Log Publisher is enabled for use.
+property.java-class.synopsis=The fully-qualified name of the Java class that provides the File Based Audit Log Publisher implementation.
+property.log-file.synopsis=The file name to use for the log files generated by the File Based Audit Log Publisher. The path to the file is relative to the server root.
+property.log-file.syntax.string.pattern.synopsis=A path to an existing file that is readable by the server.
+property.log-file-permissions.synopsis=The UNIX permissions of the log files created by this File Based Audit Log Publisher.
+property.log-file-permissions.syntax.string.pattern.synopsis=A valid UNIX mode string. The mode string must contain three digits between zero and seven.
+property.queue-size.synopsis=The maximum number of log records that can be stored in the asynchronous queue.
+property.queue-size.description=Setting the queue size to zero activates parallel log writer implementation which has no queue size limit and as such the parallel log writer should only be used on a very well tuned server configuration to avoid potential out of memory errors.
+property.queue-size.requires-admin-action.synopsis=The File Based Audit Log Publisher must be restarted if this property is changed and the asynchronous property is set to true.
+property.retention-policy.synopsis=The retention policy to use for the File Based Audit Log Publisher .
+property.retention-policy.description=When multiple policies are used, log files are cleaned when any of the policy's conditions are met.
+property.retention-policy.default-behavior.alias.synopsis=No retention policy is used and log files are never cleaned.
+property.rotation-policy.synopsis=The rotation policy to use for the File Based Audit Log Publisher .
+property.rotation-policy.description=When multiple policies are used, rotation will occur if any policy's conditions are met.
+property.rotation-policy.default-behavior.alias.synopsis=No rotation policy is used and log rotation will not occur.
+property.suppress-internal-operations.synopsis=Indicates whether internal operations (for example, operations that are initiated by plugins) should be logged along with the operations that are requested by users.
+property.suppress-synchronization-operations.synopsis=Indicates whether access messages that are generated by synchronization operations should be suppressed.
+property.time-interval.synopsis=Specifies the interval at which to check whether the log files need to be rotated.
diff --git a/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java b/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
index 22806ee..6a0e94b 100644
--- a/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
+++ b/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
@@ -30,35 +30,28 @@
 
 
 import static org.opends.messages.ConfigMessages.*;
-import static org.opends.server.util.StaticUtils.*;
+import static org.opends.server.util.StaticUtils.getFileForPath;
+import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
+import java.util.*;
 
 import org.opends.messages.Message;
 import org.opends.messages.MessageBuilder;
+import org.opends.server.admin.server.ConfigurationAddListener;
 import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.server.ConfigurationDeleteListener;
+import org.opends.server.admin.std.meta.AccessLogFilteringCriteriaCfgDefn.*;
+import org.opends.server.admin.std.meta.FileBasedAccessLogPublisherCfgDefn.*;
+import org.opends.server.admin.std.server.AccessLogFilteringCriteriaCfg;
 import org.opends.server.admin.std.server.AccessLogPublisherCfg;
 import org.opends.server.admin.std.server.FileBasedAccessLogPublisherCfg;
 import org.opends.server.api.AccessLogPublisher;
 import org.opends.server.api.ClientConnection;
 import org.opends.server.api.ExtendedOperationHandler;
 import org.opends.server.config.ConfigException;
-import org.opends.server.core.AbandonOperation;
-import org.opends.server.core.AddOperation;
-import org.opends.server.core.BindOperation;
-import org.opends.server.core.CompareOperation;
-import org.opends.server.core.DeleteOperation;
-import org.opends.server.core.DirectoryServer;
-import org.opends.server.core.ExtendedOperation;
-import org.opends.server.core.ModifyDNOperation;
-import org.opends.server.core.ModifyOperation;
-import org.opends.server.core.SearchOperation;
-import org.opends.server.core.UnbindOperation;
+import org.opends.server.core.*;
 import org.opends.server.types.*;
 import org.opends.server.util.TimeThread;
 
@@ -74,11 +67,184 @@
 {
 
   /**
+   * Criteria based filter.
+   */
+  static final class CriteriaFilter implements Filter
+  {
+    private final AccessLogFilteringCriteriaCfg cfg;
+    private final boolean logConnectRecords;
+    private final boolean logDisconnectRecords;
+    private final EnumSet<OperationType> logOperationRecords;
+
+
+
+    /**
+     * Creates a new criteria based filter.
+     *
+     * @param cfg
+     *          The access log filter criteria.
+     */
+    CriteriaFilter(final AccessLogFilteringCriteriaCfg cfg)
+    {
+      this.cfg = cfg;
+
+      // Pre-parse the log record types for more efficient queries.
+      if (cfg.getLogRecordType().isEmpty())
+      {
+        logConnectRecords = true;
+        logDisconnectRecords = true;
+
+        logOperationRecords = EnumSet.allOf(OperationType.class);
+      }
+      else
+      {
+        logConnectRecords =
+          cfg.getLogRecordType().contains(LogRecordType.CONNECT);
+        logDisconnectRecords =
+          cfg.getLogRecordType().contains(LogRecordType.DISCONNECT);
+
+        logOperationRecords = EnumSet.noneOf(OperationType.class);
+        for (final LogRecordType type : cfg.getLogRecordType())
+        {
+          switch (type)
+          {
+          case ABANDON:
+            logOperationRecords.add(OperationType.ABANDON);
+            break;
+          case ADD:
+            logOperationRecords.add(OperationType.ADD);
+            break;
+          case BIND:
+            logOperationRecords.add(OperationType.BIND);
+            break;
+          case COMPARE:
+            logOperationRecords.add(OperationType.COMPARE);
+            break;
+          case DELETE:
+            logOperationRecords.add(OperationType.DELETE);
+            break;
+          case EXTENDED:
+            logOperationRecords.add(OperationType.EXTENDED);
+            break;
+          case MODIFY:
+            logOperationRecords.add(OperationType.MODIFY);
+            break;
+          case RENAME:
+            logOperationRecords.add(OperationType.MODIFY_DN);
+            break;
+          case SEARCH:
+            logOperationRecords.add(OperationType.SEARCH);
+            break;
+          case UNBIND:
+            logOperationRecords.add(OperationType.UNBIND);
+            break;
+          default: // Ignore CONNECT/DISCONNECT
+            break;
+          }
+        }
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isConnectLoggable(final ClientConnection connection)
+    {
+      if (!logConnectRecords)
+      {
+        return false;
+      }
+
+      // TODO: other checks.
+
+      return true;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isDisconnectLoggable(final ClientConnection connection)
+    {
+      if (!logDisconnectRecords)
+      {
+        return false;
+      }
+
+      // TODO: other checks.
+
+      return true;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isRequestLoggable(final Operation operation)
+    {
+      if (!logOperationRecords.contains(operation.getOperationType()))
+      {
+        return false;
+      }
+
+      // TODO: other checks.
+
+      return true;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isResponseLoggable(final Operation operation)
+    {
+      if (!logOperationRecords.contains(operation.getOperationType()))
+      {
+        return false;
+      }
+
+      // TODO: other checks.
+
+      return true;
+    }
+
+  }
+
+
+
+  /**
    * Log message filter predicate.
    */
   static interface Filter
   {
     /**
+     * Returns {@code true} if the provided client connect should be logged.
+     *
+     * @param connection
+     *          The client connection.
+     * @return {@code true} if the provided client connect should be logged.
+     */
+    boolean isConnectLoggable(ClientConnection connection);
+
+
+
+    /**
+     * Returns {@code true} if the provided client disconnect should be logged.
+     *
+     * @param connection
+     *          The client connection.
+     * @return {@code true} if the provided client disconnect should be logged.
+     */
+    boolean isDisconnectLoggable(ClientConnection connection);
+
+
+
+    /**
      * Returns {@code true} if the provided request should be logged.
      *
      * @param operation
@@ -102,66 +268,6 @@
 
 
   /**
-   * A filter which performs a logical AND over a set of sub-filters.
-   */
-  static final class AndFilter implements Filter
-  {
-    private final Filter[] subFilters;
-
-
-
-    /**
-     * Creates a new AND filter.
-     *
-     * @param subFilters
-     *          The sub-filters.
-     */
-    AndFilter(Filter[] subFilters)
-    {
-      this.subFilters = subFilters;
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean isRequestLoggable(Operation operation)
-    {
-      for (Filter filter : subFilters)
-      {
-        if (!filter.isRequestLoggable(operation))
-        {
-          // Fail fast.
-          return false;
-        }
-      }
-      return true;
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean isResponseLoggable(Operation operation)
-    {
-      for (Filter filter : subFilters)
-      {
-        if (!filter.isResponseLoggable(operation))
-        {
-          // Fail fast.
-          return false;
-        }
-      }
-      return true;
-    }
-
-  }
-
-
-
-  /**
    * A filter which performs a logical OR over a set of sub-filters.
    */
   static final class OrFilter implements Filter
@@ -176,7 +282,7 @@
      * @param subFilters
      *          The sub-filters.
      */
-    OrFilter(Filter[] subFilters)
+    OrFilter(final Filter[] subFilters)
     {
       this.subFilters = subFilters;
     }
@@ -186,9 +292,45 @@
     /**
      * {@inheritDoc}
      */
-    public boolean isRequestLoggable(Operation operation)
+    public boolean isConnectLoggable(final ClientConnection connection)
     {
-      for (Filter filter : subFilters)
+      for (final Filter filter : subFilters)
+      {
+        if (filter.isConnectLoggable(connection))
+        {
+          // Succeed fast.
+          return true;
+        }
+      }
+      return false;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isDisconnectLoggable(final ClientConnection connection)
+    {
+      for (final Filter filter : subFilters)
+      {
+        if (filter.isDisconnectLoggable(connection))
+        {
+          // Succeed fast.
+          return true;
+        }
+      }
+      return false;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isRequestLoggable(final Operation operation)
+    {
+      for (final Filter filter : subFilters)
       {
         if (filter.isRequestLoggable(operation))
         {
@@ -204,9 +346,9 @@
     /**
      * {@inheritDoc}
      */
-    public boolean isResponseLoggable(Operation operation)
+    public boolean isResponseLoggable(final Operation operation)
     {
-      for (Filter filter : subFilters)
+      for (final Filter filter : subFilters)
       {
         if (filter.isResponseLoggable(operation))
         {
@@ -225,20 +367,34 @@
    * The root filter which first checks the logger configuration, delegating to
    * a sub-filter if needed.
    */
-  final class RootFilter implements Filter
+  static final class RootFilter implements Filter
   {
     private final Filter subFilter;
+    private final boolean suppressInternalOperations;
+    private final boolean suppressSynchronizationOperations;
+    private final FilteringPolicy policy;
 
 
 
     /**
      * Creates a new root filter.
      *
+     * @param suppressInternal
+     *          Indicates whether internal operations should be suppressed.
+     * @param suppressSynchronization
+     *          Indicates whether sync operations should be suppressed.
+     * @param policy
+     *          The filtering policy.
      * @param subFilter
-     *          The sub-filter.
+     *          The sub-filters.
      */
-    RootFilter(Filter subFilter)
+    RootFilter(final boolean suppressInternal,
+        final boolean suppressSynchronization, final FilteringPolicy policy,
+        final Filter subFilter)
     {
+      this.suppressInternalOperations = suppressInternal;
+      this.suppressSynchronizationOperations = suppressSynchronization;
+      this.policy = policy;
       this.subFilter = subFilter;
     }
 
@@ -247,12 +403,20 @@
     /**
      * {@inheritDoc}
      */
-    public boolean isRequestLoggable(Operation operation)
+    public boolean isConnectLoggable(final ClientConnection connection)
     {
-      if (isLoggable(operation))
+      final long connectionID = connection.getConnectionID();
+      if (connectionID >= 0 || !suppressInternalOperations)
       {
-        // FIXME: actual behavior determined by default filter policy.
-        return subFilter.isRequestLoggable(operation);
+        switch (policy)
+        {
+        case INCLUSIVE:
+          return subFilter.isConnectLoggable(connection);
+        case EXCLUSIVE:
+          return !subFilter.isConnectLoggable(connection);
+        default: // NO_FILTERING:
+          return true;
+        }
       }
       else
       {
@@ -265,12 +429,70 @@
     /**
      * {@inheritDoc}
      */
-    public boolean isResponseLoggable(Operation operation)
+    public boolean isDisconnectLoggable(final ClientConnection connection)
+    {
+      final long connectionID = connection.getConnectionID();
+      if (connectionID >= 0 || !suppressInternalOperations)
+      {
+        switch (policy)
+        {
+        case INCLUSIVE:
+          return subFilter.isDisconnectLoggable(connection);
+        case EXCLUSIVE:
+          return !subFilter.isDisconnectLoggable(connection);
+        default: // NO_FILTERING:
+          return true;
+        }
+      }
+      else
+      {
+        return false;
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isRequestLoggable(final Operation operation)
     {
       if (isLoggable(operation))
       {
-        // FIXME: actual behavior determined by default filter policy.
-        return subFilter.isResponseLoggable(operation);
+        switch (policy)
+        {
+        case INCLUSIVE:
+          return subFilter.isRequestLoggable(operation);
+        case EXCLUSIVE:
+          return !subFilter.isRequestLoggable(operation);
+        default: // NO_FILTERING:
+          return true;
+        }
+      }
+      else
+      {
+        return false;
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isResponseLoggable(final Operation operation)
+    {
+      if (isLoggable(operation))
+      {
+        switch (policy)
+        {
+        case INCLUSIVE:
+          return subFilter.isResponseLoggable(operation);
+        case EXCLUSIVE:
+          return !subFilter.isResponseLoggable(operation);
+        default: // NO_FILTERING:
+          return true;
+        }
       }
       else
       {
@@ -281,9 +503,9 @@
 
 
     // Determines whether the provided operation should be logged.
-    private boolean isLoggable(Operation operation)
+    private boolean isLoggable(final Operation operation)
     {
-      long connectionID = operation.getConnectionID();
+      final long connectionID = operation.getConnectionID();
       if (connectionID < 0)
       {
         // This is an internal operation.
@@ -304,6 +526,92 @@
 
 
   /**
+   * Filter criteria configuration listener.
+   */
+  private final class FilterChangeListener implements
+      ConfigurationChangeListener<AccessLogFilteringCriteriaCfg>,
+      ConfigurationAddListener<AccessLogFilteringCriteriaCfg>,
+      ConfigurationDeleteListener<AccessLogFilteringCriteriaCfg>
+  {
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConfigChangeResult applyConfigurationAdd(
+        final AccessLogFilteringCriteriaCfg configuration)
+    {
+      // Rebuild the filter using the new configuration and criteria.
+      buildFilters();
+      configuration.addChangeListener(this);
+      return new ConfigChangeResult(ResultCode.SUCCESS, false);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConfigChangeResult applyConfigurationChange(
+        final AccessLogFilteringCriteriaCfg configuration)
+    {
+      // Rebuild the filter using the new configuration and criteria.
+      buildFilters();
+      return new ConfigChangeResult(ResultCode.SUCCESS, false);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConfigChangeResult applyConfigurationDelete(
+        final AccessLogFilteringCriteriaCfg configuration)
+    {
+      // Rebuild the filter using the new configuration and criteria.
+      buildFilters();
+      return new ConfigChangeResult(ResultCode.SUCCESS, false);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isConfigurationAddAcceptable(
+        final AccessLogFilteringCriteriaCfg configuration,
+        final List<Message> unacceptableReasons)
+    {
+      return true;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isConfigurationChangeAcceptable(
+        final AccessLogFilteringCriteriaCfg configuration,
+        final List<Message> unacceptableReasons)
+    {
+      return true;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isConfigurationDeleteAcceptable(
+        final AccessLogFilteringCriteriaCfg configuration,
+        final List<Message> unacceptableReasons)
+    {
+      return true;
+    }
+  }
+
+
+
+  /**
    * The category to use when logging responses.
    */
   private static final String CATEGORY_RESPONSE = "RES";
@@ -328,26 +636,22 @@
    *         messages to standard out.
    */
   public static TextAccessLogPublisher getStartupTextAccessPublisher(
-      TextWriter writer, boolean suppressInternal)
+      final TextWriter writer, final boolean suppressInternal)
   {
-    TextAccessLogPublisher startupPublisher = new TextAccessLogPublisher();
+    final TextAccessLogPublisher startupPublisher =
+      new TextAccessLogPublisher();
     startupPublisher.writer = writer;
-    startupPublisher.suppressInternalOperations = suppressInternal;
-    startupPublisher.setSubFilter(new AndFilter(new Filter[0])); // Always true.
+    startupPublisher.buildFilters(suppressInternal, false,
+        FilteringPolicy.NO_FILTERING);
     return startupPublisher;
   }
 
 
 
-  private FileBasedAccessLogPublisherCfg currentConfig;
-
-  private boolean suppressInternalOperations = true;
-
-  private boolean suppressSynchronizationOperations = false;
-
-  private TextWriter writer;
-
-  private Filter filter;
+  private FileBasedAccessLogPublisherCfg currentConfig = null;
+  private TextWriter writer = null;
+  private Filter filter = null;
+  private FilterChangeListener filterChangeListener = null;
 
 
 
@@ -355,26 +659,21 @@
    * {@inheritDoc}
    */
   public ConfigChangeResult applyConfigurationChange(
-      FileBasedAccessLogPublisherCfg config)
+      final FileBasedAccessLogPublisherCfg config)
   {
     // Default result code.
     ResultCode resultCode = ResultCode.SUCCESS;
     boolean adminActionRequired = false;
-    ArrayList<Message> messages = new ArrayList<Message>();
+    final ArrayList<Message> messages = new ArrayList<Message>();
 
-    suppressInternalOperations = config.isSuppressInternalOperations();
-    suppressSynchronizationOperations = config
-        .isSuppressSynchronizationOperations();
-
-    File logFile = getFileForPath(config.getLogFile());
-    FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
-
+    final File logFile = getFileForPath(config.getLogFile());
+    final FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
     try
     {
-      FilePermission perm = FilePermission.decodeUNIXMode(config
+      final FilePermission perm = FilePermission.decodeUNIXMode(config
           .getLogFilePermissions());
 
-      boolean writerAutoFlush = config.isAutoFlush()
+      final boolean writerAutoFlush = config.isAutoFlush()
           && !config.isAsynchronous();
 
       TextWriter currentWriter;
@@ -395,7 +694,8 @@
 
       if (currentWriter instanceof MultifileTextWriter)
       {
-        MultifileTextWriter mfWriter = (MultifileTextWriter) currentWriter;
+        final MultifileTextWriter mfWriter =
+          (MultifileTextWriter) currentWriter;
 
         mfWriter.setNamingPolicy(fnPolicy);
         mfWriter.setFilePermissions(perm);
@@ -407,12 +707,12 @@
         mfWriter.removeAllRetentionPolicies();
         mfWriter.removeAllRotationPolicies();
 
-        for (DN dn : config.getRotationPolicyDNs())
+        for (final DN dn : config.getRotationPolicyDNs())
         {
           mfWriter.addRotationPolicy(DirectoryServer.getRotationPolicy(dn));
         }
 
-        for (DN dn : config.getRetentionPolicyDNs())
+        for (final DN dn : config.getRetentionPolicyDNs())
         {
           mfWriter.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn));
         }
@@ -420,7 +720,8 @@
         if (writer instanceof AsyncronousTextWriter && !config.isAsynchronous())
         {
           // The asynchronous setting is being turned off.
-          AsyncronousTextWriter asyncWriter = ((AsyncronousTextWriter) writer);
+          final AsyncronousTextWriter asyncWriter =
+            ((AsyncronousTextWriter) writer);
           writer = mfWriter;
           asyncWriter.shutdown(false);
         }
@@ -428,7 +729,7 @@
         if (writer instanceof ParallelTextWriter && !config.isAsynchronous())
         {
           // The asynchronous setting is being turned off.
-          ParallelTextWriter asyncWriter = ((ParallelTextWriter) writer);
+          final ParallelTextWriter asyncWriter = ((ParallelTextWriter) writer);
           writer = mfWriter;
           asyncWriter.shutdown(false);
         }
@@ -437,7 +738,7 @@
             && config.isAsynchronous())
         {
           // The asynchronous setting is being turned on.
-          AsyncronousTextWriter asyncWriter = new AsyncronousTextWriter(
+          final AsyncronousTextWriter asyncWriter = new AsyncronousTextWriter(
               "Asyncronous Text Writer for " + config.dn().toNormalizedString(),
               config.getQueueSize(), config.isAutoFlush(), mfWriter);
           writer = asyncWriter;
@@ -446,7 +747,7 @@
         if (!(writer instanceof ParallelTextWriter) && config.isAsynchronous())
         {
           // The asynchronous setting is being turned on.
-          ParallelTextWriter asyncWriter = new ParallelTextWriter(
+          final ParallelTextWriter asyncWriter = new ParallelTextWriter(
               "Parallel Text Writer for " + config.dn().toNormalizedString(),
               config.isAutoFlush(), mfWriter);
           writer = asyncWriter;
@@ -458,16 +759,16 @@
           adminActionRequired = true;
         }
 
-        // FIXME: use a dummy set of sub-filters for now.
-        setSubFilter(new AndFilter(new Filter[0])); // Always true.
-
         currentConfig = config;
+
+        // Rebuild the filter using the new configuration and criteria.
+        buildFilters();
       }
     }
-    catch (Exception e)
+    catch (final Exception e)
     {
-      Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(config.dn()
-          .toString(), stackTraceToSingleLineString(e));
+      final Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(
+          config.dn().toString(), stackTraceToSingleLineString(e));
       resultCode = DirectoryServer.getServerErrorResultCode();
       messages.add(message);
 
@@ -489,6 +790,24 @@
     if (currentConfig != null)
     {
       currentConfig.removeFileBasedAccessChangeListener(this);
+
+      for (final String criteriaName : currentConfig
+          .listAccessLogFilteringCriteria())
+      {
+        try
+        {
+          currentConfig.getAccessLogFilteringCriteria(criteriaName)
+              .removeChangeListener(filterChangeListener);
+        }
+        catch (final ConfigException e)
+        {
+          // Ignore.
+        }
+      }
+      currentConfig
+          .removeAccessLogFilteringCriteriaAddListener(filterChangeListener);
+      currentConfig
+          .removeAccessLogFilteringCriteriaDeleteListener(filterChangeListener);
     }
   }
 
@@ -516,35 +835,36 @@
    * {@inheritDoc}
    */
   @Override
-  public void initializeAccessLogPublisher(FileBasedAccessLogPublisherCfg cfg)
-      throws ConfigException, InitializationException
+  public void initializeAccessLogPublisher(
+      final FileBasedAccessLogPublisherCfg cfg) throws ConfigException,
+      InitializationException
   {
-    File logFile = getFileForPath(cfg.getLogFile());
-    FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
+    final File logFile = getFileForPath(cfg.getLogFile());
+    final FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
 
     try
     {
-      FilePermission perm = FilePermission.decodeUNIXMode(cfg
+      final FilePermission perm = FilePermission.decodeUNIXMode(cfg
           .getLogFilePermissions());
 
-      LogPublisherErrorHandler errorHandler = new LogPublisherErrorHandler(
-          cfg.dn());
+      final LogPublisherErrorHandler errorHandler =
+        new LogPublisherErrorHandler(cfg.dn());
 
-      boolean writerAutoFlush = cfg.isAutoFlush()
+      final boolean writerAutoFlush = cfg.isAutoFlush()
           && !cfg.isAsynchronous();
 
-      MultifileTextWriter writer = new MultifileTextWriter(
+      final MultifileTextWriter writer = new MultifileTextWriter(
           "Multifile Text Writer for " + cfg.dn().toNormalizedString(),
           cfg.getTimeInterval(), fnPolicy, perm, errorHandler, "UTF-8",
           writerAutoFlush, cfg.isAppend(), (int) cfg.getBufferSize());
 
       // Validate retention and rotation policies.
-      for (DN dn : cfg.getRotationPolicyDNs())
+      for (final DN dn : cfg.getRotationPolicyDNs())
       {
         writer.addRotationPolicy(DirectoryServer.getRotationPolicy(dn));
       }
 
-      for (DN dn : cfg.getRetentionPolicyDNs())
+      for (final DN dn : cfg.getRetentionPolicyDNs())
       {
         writer.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn));
       }
@@ -568,30 +888,47 @@
         this.writer = writer;
       }
     }
-    catch (DirectoryException e)
+    catch (final DirectoryException e)
     {
-      Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(cfg.dn()
-          .toString(), String.valueOf(e));
+      final Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(cfg
+          .dn().toString(), String.valueOf(e));
       throw new InitializationException(message, e);
 
     }
-    catch (IOException e)
+    catch (final IOException e)
     {
-      Message message = ERR_CONFIG_LOGGING_CANNOT_OPEN_FILE.get(
+      final Message message = ERR_CONFIG_LOGGING_CANNOT_OPEN_FILE.get(
           logFile.toString(), cfg.dn().toString(), String.valueOf(e));
       throw new InitializationException(message, e);
 
     }
 
-    suppressInternalOperations = cfg.isSuppressInternalOperations();
-    suppressSynchronizationOperations = cfg
-        .isSuppressSynchronizationOperations();
     currentConfig = cfg;
 
-    // FIXME: use a dummy set of sub-filters for now.
-    setSubFilter(new AndFilter(new Filter[0])); // Always true.
+    // Rebuild the filter using the new configuration and criteria.
+    buildFilters();
 
-    cfg.addFileBasedAccessChangeListener(this);
+    // Add change listeners.
+    filterChangeListener = new FilterChangeListener();
+    for (final String criteriaName : currentConfig
+        .listAccessLogFilteringCriteria())
+    {
+      try
+      {
+        currentConfig.getAccessLogFilteringCriteria(criteriaName)
+            .addChangeListener(filterChangeListener);
+      }
+      catch (final ConfigException e)
+      {
+        // Ignore.
+      }
+    }
+    currentConfig
+        .addAccessLogFilteringCriteriaAddListener(filterChangeListener);
+    currentConfig
+        .addAccessLogFilteringCriteriaDeleteListener(filterChangeListener);
+
+    currentConfig.addFileBasedAccessChangeListener(this);
   }
 
 
@@ -600,10 +937,11 @@
    * {@inheritDoc}
    */
   @Override
-  public boolean isConfigurationAcceptable(AccessLogPublisherCfg configuration,
-      List<Message> unacceptableReasons)
+  public boolean isConfigurationAcceptable(
+      final AccessLogPublisherCfg configuration,
+      final List<Message> unacceptableReasons)
   {
-    FileBasedAccessLogPublisherCfg config =
+    final FileBasedAccessLogPublisherCfg config =
       (FileBasedAccessLogPublisherCfg) configuration;
     return isConfigurationChangeAcceptable(config, unacceptableReasons);
   }
@@ -614,24 +952,25 @@
    * {@inheritDoc}
    */
   public boolean isConfigurationChangeAcceptable(
-      FileBasedAccessLogPublisherCfg config, List<Message> unacceptableReasons)
+      final FileBasedAccessLogPublisherCfg config,
+      final List<Message> unacceptableReasons)
   {
     // Make sure the permission is valid.
     try
     {
-      FilePermission filePerm = FilePermission.decodeUNIXMode(config
+      final FilePermission filePerm = FilePermission.decodeUNIXMode(config
           .getLogFilePermissions());
       if (!filePerm.isOwnerWritable())
       {
-        Message message = ERR_CONFIG_LOGGING_INSANE_MODE.get(config
+        final Message message = ERR_CONFIG_LOGGING_INSANE_MODE.get(config
             .getLogFilePermissions());
         unacceptableReasons.add(message);
         return false;
       }
     }
-    catch (DirectoryException e)
+    catch (final DirectoryException e)
     {
-      Message message = ERR_CONFIG_LOGGING_MODE_INVALID.get(
+      final Message message = ERR_CONFIG_LOGGING_MODE_INVALID.get(
           config.getLogFilePermissions(), String.valueOf(e));
       unacceptableReasons.add(message);
       return false;
@@ -651,19 +990,21 @@
    *          abandon request.
    */
   @Override
-  public void logAbandonRequest(AbandonOperation abandonOperation)
+  public void logAbandonRequest(final AbandonOperation abandonOperation)
   {
     if (!filter.isRequestLoggable(abandonOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(abandonOperation, "ABANDON", CATEGORY_REQUEST, buffer);
     buffer.append(" idToAbandon=");
     buffer.append(abandonOperation.getIDToAbandon());
     if (abandonOperation.isSynchronizationOperation())
+    {
       buffer.append(" type=synchronization");
+    }
 
     writer.writeRecord(buffer.toString());
   }
@@ -679,18 +1020,18 @@
    *          abandon request.
    */
   @Override
-  public void logAbandonResult(AbandonOperation abandonOperation)
+  public void logAbandonResult(final AbandonOperation abandonOperation)
   {
     if (!filter.isResponseLoggable(abandonOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(abandonOperation, "ABANDON", CATEGORY_RESPONSE, buffer);
     buffer.append(" result=");
     buffer.append(abandonOperation.getResultCode().getIntValue());
-    MessageBuilder msg = abandonOperation.getErrorMessage();
+    final MessageBuilder msg = abandonOperation.getErrorMessage();
     if ((msg != null) && (msg.length() > 0))
     {
       buffer.append(" message=\"");
@@ -717,20 +1058,22 @@
    *          request.
    */
   @Override
-  public void logAddRequest(AddOperation addOperation)
+  public void logAddRequest(final AddOperation addOperation)
   {
     if (!filter.isRequestLoggable(addOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(addOperation, "ADD", CATEGORY_REQUEST, buffer);
     buffer.append(" dn=\"");
     buffer.append(addOperation.getRawEntryDN().toString());
     buffer.append("\"");
     if (addOperation.isSynchronizationOperation())
+    {
       buffer.append(" type=synchronization");
+    }
 
     writer.writeRecord(buffer.toString());
   }
@@ -746,19 +1089,19 @@
    *          response.
    */
   @Override
-  public void logAddResponse(AddOperation addOperation)
+  public void logAddResponse(final AddOperation addOperation)
   {
     if (!filter.isResponseLoggable(addOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(addOperation, "ADD", CATEGORY_RESPONSE, buffer);
     buffer.append(" result=");
     buffer.append(addOperation.getResultCode().getIntValue());
 
-    MessageBuilder msg = addOperation.getErrorMessage();
+    final MessageBuilder msg = addOperation.getErrorMessage();
     if ((msg != null) && (msg.length() > 0))
     {
       buffer.append(" message=\"");
@@ -768,7 +1111,7 @@
 
     logAdditionalLogItems(addOperation, buffer);
 
-    DN proxiedAuthDN = addOperation.getProxiedAuthorizationDN();
+    final DN proxiedAuthDN = addOperation.getProxiedAuthorizationDN();
     if (proxiedAuthDN != null)
     {
       buffer.append(" authzDN=\"");
@@ -798,14 +1141,14 @@
    *          request.
    */
   @Override
-  public void logBindRequest(BindOperation bindOperation)
+  public void logBindRequest(final BindOperation bindOperation)
   {
     if (!filter.isRequestLoggable(bindOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(bindOperation, "BIND", CATEGORY_REQUEST, buffer);
 
     final String protocolVersion = bindOperation.getProtocolVersion();
@@ -834,7 +1177,9 @@
     buffer.append(bindOperation.getRawBindDN().toString());
     buffer.append("\"");
     if (bindOperation.isSynchronizationOperation())
+    {
       buffer.append(" type=synchronization");
+    }
 
     writer.writeRecord(buffer.toString());
   }
@@ -850,19 +1195,19 @@
    *          bind response.
    */
   @Override
-  public void logBindResponse(BindOperation bindOperation)
+  public void logBindResponse(final BindOperation bindOperation)
   {
     if (!filter.isResponseLoggable(bindOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(bindOperation, "BIND", CATEGORY_RESPONSE, buffer);
     buffer.append(" result=");
     buffer.append(bindOperation.getResultCode().getIntValue());
 
-    MessageBuilder msg = bindOperation.getErrorMessage();
+    final MessageBuilder msg = bindOperation.getErrorMessage();
     if ((msg != null) && (msg.length() > 0))
     {
       buffer.append(" message=\"");
@@ -870,7 +1215,7 @@
       buffer.append('\"');
     }
 
-    Message failureMessage = bindOperation.getAuthFailureReason();
+    final Message failureMessage = bindOperation.getAuthFailureReason();
     if (failureMessage != null)
     {
       buffer.append(" authFailureID=");
@@ -884,17 +1229,17 @@
 
     if (bindOperation.getResultCode() == ResultCode.SUCCESS)
     {
-      AuthenticationInfo authInfo = bindOperation.getAuthenticationInfo();
+      final AuthenticationInfo authInfo = bindOperation.getAuthenticationInfo();
       if (authInfo != null)
       {
-        DN authDN = authInfo.getAuthenticationDN();
+        final DN authDN = authInfo.getAuthenticationDN();
         if (authDN != null)
         {
           buffer.append(" authDN=\"");
           authDN.toString(buffer);
           buffer.append('\"');
 
-          DN authzDN = authInfo.getAuthorizationDN();
+          final DN authzDN = authInfo.getAuthorizationDN();
           if (!authDN.equals(authzDN))
           {
             buffer.append(" authzDN=\"");
@@ -934,21 +1279,23 @@
    *          compare request.
    */
   @Override
-  public void logCompareRequest(CompareOperation compareOperation)
+  public void logCompareRequest(final CompareOperation compareOperation)
   {
     if (!filter.isRequestLoggable(compareOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(compareOperation, "COMPARE", CATEGORY_REQUEST, buffer);
     buffer.append(" dn=\"");
     buffer.append(compareOperation.getRawEntryDN().toString());
     buffer.append("\" attr=");
     buffer.append(compareOperation.getAttributeType().getNameOrOID());
     if (compareOperation.isSynchronizationOperation())
+    {
       buffer.append(" type=synchronization");
+    }
 
     writer.writeRecord(buffer.toString());
   }
@@ -964,19 +1311,19 @@
    *          compare response.
    */
   @Override
-  public void logCompareResponse(CompareOperation compareOperation)
+  public void logCompareResponse(final CompareOperation compareOperation)
   {
     if (!filter.isResponseLoggable(compareOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(compareOperation, "COMPARE", CATEGORY_RESPONSE, buffer);
     buffer.append(" result=");
     buffer.append(compareOperation.getResultCode().getIntValue());
 
-    MessageBuilder msg = compareOperation.getErrorMessage();
+    final MessageBuilder msg = compareOperation.getErrorMessage();
     if ((msg != null) && (msg.length() > 0))
     {
       buffer.append(" message=\"");
@@ -986,7 +1333,7 @@
 
     logAdditionalLogItems(compareOperation, buffer);
 
-    DN proxiedAuthDN = compareOperation.getProxiedAuthorizationDN();
+    final DN proxiedAuthDN = compareOperation.getProxiedAuthorizationDN();
     if (proxiedAuthDN != null)
     {
       buffer.append(" authzDN=\"");
@@ -1016,16 +1363,15 @@
    *          The client connection that has been established.
    */
   @Override
-  public void logConnect(ClientConnection clientConnection)
+  public void logConnect(final ClientConnection clientConnection)
   {
-    // FIXME: implement filtering.
-    long connectionID = clientConnection.getConnectionID();
-
-    if (connectionID < 0 && suppressInternalOperations)
+    if (!filter.isConnectLoggable(clientConnection))
     {
       return;
     }
-    StringBuilder buffer = new StringBuilder(100);
+
+    final long connectionID = clientConnection.getConnectionID();
+    final StringBuilder buffer = new StringBuilder(100);
     buffer.append("[");
     buffer.append(TimeThread.getLocalTime());
     buffer.append("]");
@@ -1053,20 +1399,22 @@
    *          request.
    */
   @Override
-  public void logDeleteRequest(DeleteOperation deleteOperation)
+  public void logDeleteRequest(final DeleteOperation deleteOperation)
   {
     if (!filter.isRequestLoggable(deleteOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(deleteOperation, "DELETE", CATEGORY_REQUEST, buffer);
     buffer.append(" dn=\"");
     buffer.append(deleteOperation.getRawEntryDN().toString());
     buffer.append("\"");
     if (deleteOperation.isSynchronizationOperation())
+    {
       buffer.append(" type=synchronization");
+    }
 
     writer.writeRecord(buffer.toString());
   }
@@ -1082,19 +1430,19 @@
    *          delete response.
    */
   @Override
-  public void logDeleteResponse(DeleteOperation deleteOperation)
+  public void logDeleteResponse(final DeleteOperation deleteOperation)
   {
     if (!filter.isResponseLoggable(deleteOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(deleteOperation, "DELETE", CATEGORY_RESPONSE, buffer);
     buffer.append(" result=");
     buffer.append(deleteOperation.getResultCode().getIntValue());
 
-    MessageBuilder msg = deleteOperation.getErrorMessage();
+    final MessageBuilder msg = deleteOperation.getErrorMessage();
     if ((msg != null) && (msg.length() > 0))
     {
       buffer.append(" message=\"");
@@ -1104,7 +1452,7 @@
 
     logAdditionalLogItems(deleteOperation, buffer);
 
-    DN proxiedAuthDN = deleteOperation.getProxiedAuthorizationDN();
+    final DN proxiedAuthDN = deleteOperation.getProxiedAuthorizationDN();
     if (proxiedAuthDN != null)
     {
       buffer.append(" authzDN=\"");
@@ -1138,16 +1486,16 @@
    *          about the disconnect.
    */
   @Override
-  public void logDisconnect(ClientConnection clientConnection,
-      DisconnectReason disconnectReason, Message message)
+  public void logDisconnect(final ClientConnection clientConnection,
+      final DisconnectReason disconnectReason, final Message message)
   {
-    // FIXME: implement filtering.
-    long connectionID = clientConnection.getConnectionID();
-    if (connectionID < 0 && suppressInternalOperations)
+    if (!filter.isDisconnectLoggable(clientConnection))
     {
       return;
     }
-    StringBuilder buffer = new StringBuilder(100);
+
+    final long connectionID = clientConnection.getConnectionID();
+    final StringBuilder buffer = new StringBuilder(100);
     buffer.append("[");
     buffer.append(TimeThread.getLocalTime());
     buffer.append("]");
@@ -1178,7 +1526,7 @@
    *          the extended request.
    */
   @Override
-  public void logExtendedRequest(ExtendedOperation extendedOperation)
+  public void logExtendedRequest(final ExtendedOperation extendedOperation)
   {
     if (!filter.isRequestLoggable(extendedOperation))
     {
@@ -1186,10 +1534,10 @@
     }
 
     String name = null;
-    String oid = extendedOperation.getRequestOID();
-    StringBuilder buffer = new StringBuilder(100);
+    final String oid = extendedOperation.getRequestOID();
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(extendedOperation, "EXTENDED", CATEGORY_REQUEST, buffer);
-    ExtendedOperationHandler<?> extOpHandler = DirectoryServer
+    final ExtendedOperationHandler<?> extOpHandler = DirectoryServer
         .getExtendedOperationHandler(oid);
     if (extOpHandler != null)
     {
@@ -1223,21 +1571,21 @@
    *          extended response.
    */
   @Override
-  public void logExtendedResponse(ExtendedOperation extendedOperation)
+  public void logExtendedResponse(final ExtendedOperation extendedOperation)
   {
     if (!filter.isResponseLoggable(extendedOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(extendedOperation, "EXTENDED", CATEGORY_RESPONSE, buffer);
 
     String name = null;
-    String oid = extendedOperation.getResponseOID();
+    final String oid = extendedOperation.getResponseOID();
     if (oid != null)
     {
-      ExtendedOperationHandler<?> extOpHandler = DirectoryServer
+      final ExtendedOperationHandler<?> extOpHandler = DirectoryServer
           .getExtendedOperationHandler(oid);
       if (extOpHandler != null)
       {
@@ -1257,7 +1605,7 @@
     buffer.append(" result=");
     buffer.append(extendedOperation.getResultCode().getIntValue());
 
-    MessageBuilder msg = extendedOperation.getErrorMessage();
+    final MessageBuilder msg = extendedOperation.getErrorMessage();
     if ((msg != null) && (msg.length() > 0))
     {
       buffer.append(" message=\"");
@@ -1289,14 +1637,14 @@
    *          modify DN request.
    */
   @Override
-  public void logModifyDNRequest(ModifyDNOperation modifyDNOperation)
+  public void logModifyDNRequest(final ModifyDNOperation modifyDNOperation)
   {
     if (!filter.isRequestLoggable(modifyDNOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(modifyDNOperation, "MODIFYDN", CATEGORY_REQUEST, buffer);
     buffer.append(" dn=\"");
     buffer.append(modifyDNOperation.getRawEntryDN().toString());
@@ -1305,14 +1653,16 @@
     buffer.append("\" deleteOldRDN=");
     buffer.append(modifyDNOperation.deleteOldRDN());
 
-    ByteString newSuperior = modifyDNOperation.getRawNewSuperior();
+    final ByteString newSuperior = modifyDNOperation.getRawNewSuperior();
     if (newSuperior != null)
     {
       buffer.append(" newSuperior=\"");
       buffer.append(newSuperior.toString());
     }
     if (modifyDNOperation.isSynchronizationOperation())
+    {
       buffer.append(" type=synchronization");
+    }
 
     writer.writeRecord(buffer.toString());
   }
@@ -1328,19 +1678,19 @@
    *          the modify DN response.
    */
   @Override
-  public void logModifyDNResponse(ModifyDNOperation modifyDNOperation)
+  public void logModifyDNResponse(final ModifyDNOperation modifyDNOperation)
   {
     if (!filter.isResponseLoggable(modifyDNOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(modifyDNOperation, "MODIFYDN", CATEGORY_RESPONSE, buffer);
     buffer.append(" result=");
     buffer.append(modifyDNOperation.getResultCode().getIntValue());
 
-    MessageBuilder msg = modifyDNOperation.getErrorMessage();
+    final MessageBuilder msg = modifyDNOperation.getErrorMessage();
     if ((msg != null) && (msg.length() > 0))
     {
       buffer.append(" message=\"");
@@ -1350,7 +1700,7 @@
 
     logAdditionalLogItems(modifyDNOperation, buffer);
 
-    DN proxiedAuthDN = modifyDNOperation.getProxiedAuthorizationDN();
+    final DN proxiedAuthDN = modifyDNOperation.getProxiedAuthorizationDN();
     if (proxiedAuthDN != null)
     {
       buffer.append(" authzDN=\"");
@@ -1380,20 +1730,22 @@
    *          modify request.
    */
   @Override
-  public void logModifyRequest(ModifyOperation modifyOperation)
+  public void logModifyRequest(final ModifyOperation modifyOperation)
   {
     if (!filter.isRequestLoggable(modifyOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(modifyOperation, "MODIFY", CATEGORY_REQUEST, buffer);
     buffer.append(" dn=\"");
     buffer.append(modifyOperation.getRawEntryDN().toString());
     buffer.append("\"");
     if (modifyOperation.isSynchronizationOperation())
+    {
       buffer.append(" type=synchronization");
+    }
 
     writer.writeRecord(buffer.toString());
   }
@@ -1409,19 +1761,19 @@
    *          modify response.
    */
   @Override
-  public void logModifyResponse(ModifyOperation modifyOperation)
+  public void logModifyResponse(final ModifyOperation modifyOperation)
   {
     if (!filter.isResponseLoggable(modifyOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(modifyOperation, "MODIFY", CATEGORY_RESPONSE, buffer);
     buffer.append(" result=");
     buffer.append(modifyOperation.getResultCode().getIntValue());
 
-    MessageBuilder msg = modifyOperation.getErrorMessage();
+    final MessageBuilder msg = modifyOperation.getErrorMessage();
     if ((msg != null) && (msg.length() > 0))
     {
       buffer.append(" message=\"");
@@ -1431,7 +1783,7 @@
 
     logAdditionalLogItems(modifyOperation, buffer);
 
-    DN proxiedAuthDN = modifyOperation.getProxiedAuthorizationDN();
+    final DN proxiedAuthDN = modifyOperation.getProxiedAuthorizationDN();
     if (proxiedAuthDN != null)
     {
       buffer.append(" authzDN=\"");
@@ -1461,14 +1813,14 @@
    *          request.
    */
   @Override
-  public void logSearchRequest(SearchOperation searchOperation)
+  public void logSearchRequest(final SearchOperation searchOperation)
   {
     if (!filter.isRequestLoggable(searchOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(192);
+    final StringBuilder buffer = new StringBuilder(192);
     appendHeader(searchOperation, "SEARCH", CATEGORY_REQUEST, buffer);
     buffer.append(" base=\"");
     buffer.append(searchOperation.getRawBaseDN().toString());
@@ -1477,7 +1829,7 @@
     buffer.append(" filter=\"");
     searchOperation.getRawFilter().toString(buffer);
 
-    LinkedHashSet<String> attrs = searchOperation.getAttributes();
+    final LinkedHashSet<String> attrs = searchOperation.getAttributes();
     if ((attrs == null) || attrs.isEmpty())
     {
       buffer.append("\" attrs=\"ALL\"");
@@ -1486,7 +1838,7 @@
     {
       buffer.append("\" attrs=\"");
 
-      Iterator<String> iterator = attrs.iterator();
+      final Iterator<String> iterator = attrs.iterator();
       buffer.append(iterator.next());
       while (iterator.hasNext())
       {
@@ -1497,7 +1849,9 @@
       buffer.append("\"");
     }
     if (searchOperation.isSynchronizationOperation())
+    {
       buffer.append(" type=synchronization");
+    }
 
     writer.writeRecord(buffer.toString());
   }
@@ -1513,19 +1867,19 @@
    *          search result done message.
    */
   @Override
-  public void logSearchResultDone(SearchOperation searchOperation)
+  public void logSearchResultDone(final SearchOperation searchOperation)
   {
     if (!filter.isResponseLoggable(searchOperation))
     {
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(128);
+    final StringBuilder buffer = new StringBuilder(128);
     appendHeader(searchOperation, "SEARCH", CATEGORY_RESPONSE, buffer);
     buffer.append(" result=");
     buffer.append(searchOperation.getResultCode().getIntValue());
 
-    MessageBuilder msg = searchOperation.getErrorMessage();
+    final MessageBuilder msg = searchOperation.getErrorMessage();
     if ((msg != null) && (msg.length() > 0))
     {
       buffer.append(" message=\"");
@@ -1538,7 +1892,7 @@
 
     logAdditionalLogItems(searchOperation, buffer);
 
-    DN proxiedAuthDN = searchOperation.getProxiedAuthorizationDN();
+    final DN proxiedAuthDN = searchOperation.getProxiedAuthorizationDN();
     if (proxiedAuthDN != null)
     {
       buffer.append(" authzDN=\"");
@@ -1568,7 +1922,7 @@
    *          request.
    */
   @Override
-  public void logUnbind(UnbindOperation unbindOperation)
+  public void logUnbind(final UnbindOperation unbindOperation)
   {
     // FIXME: ensure that these are logged in combined mode.
     if (!filter.isRequestLoggable(unbindOperation))
@@ -1576,10 +1930,12 @@
       return;
     }
 
-    StringBuilder buffer = new StringBuilder(100);
+    final StringBuilder buffer = new StringBuilder(100);
     appendHeader(unbindOperation, "UNBIND", CATEGORY_REQUEST, buffer);
     if (unbindOperation.isSynchronizationOperation())
+    {
       buffer.append(" type=synchronization");
+    }
 
     writer.writeRecord(buffer.toString());
   }
@@ -1587,8 +1943,8 @@
 
 
   // Appends the common log header information to the provided buffer.
-  private void appendHeader(Operation operation, String opType,
-      String category, StringBuilder buffer)
+  private void appendHeader(final Operation operation, final String opType,
+      final String category, final StringBuilder buffer)
   {
     buffer.append('[');
     buffer.append(TimeThread.getLocalTime());
@@ -1606,21 +1962,54 @@
 
 
 
-  // Appends additional log items to the provided builder.
-  private void logAdditionalLogItems(Operation operation, StringBuilder builder)
+  // Build an appropriate set of filters based on the configuration.
+  private void buildFilters()
   {
-    for (AdditionalLogItem item : operation.getAdditionalLogItems())
+    buildFilters(currentConfig.isSuppressInternalOperations(),
+        currentConfig.isSuppressSynchronizationOperations(),
+        currentConfig.getFilteringPolicy());
+  }
+
+
+
+  private void buildFilters(final boolean suppressInternal,
+      final boolean suppressSynchronization, final FilteringPolicy policy)
+  {
+    final ArrayList<Filter> subFilters = new ArrayList<Filter>();
+    if (currentConfig != null)
+    {
+      for (final String criteriaName : currentConfig
+          .listAccessLogFilteringCriteria())
+      {
+        try
+        {
+          final AccessLogFilteringCriteriaCfg cfg = currentConfig
+              .getAccessLogFilteringCriteria(criteriaName);
+          subFilters.add(new CriteriaFilter(cfg));
+        }
+        catch (final ConfigException e)
+        {
+          // TODO: Unable to decode this access log criteria, so log a warning
+          // and continue.
+        }
+      }
+    }
+    final Filter orFilter = new OrFilter(subFilters.toArray(new Filter[0]));
+    filter = new RootFilter(suppressInternal, suppressSynchronization, policy,
+        orFilter);
+  }
+
+
+
+  // Appends additional log items to the provided builder.
+  private void logAdditionalLogItems(final Operation operation,
+      final StringBuilder builder)
+  {
+    for (final AdditionalLogItem item : operation.getAdditionalLogItems())
     {
       builder.append(' ');
       item.toString(builder);
     }
   }
 
-
-
-  // Sets the sub-filter.
-  private void setSubFilter(Filter subFilter)
-  {
-    this.filter = new RootFilter(subFilter);
-  }
 }
diff --git a/opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java b/opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java
index f059641..2e639f7 100644
--- a/opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java
+++ b/opends/src/server/org/opends/server/loggers/TextAuditLogPublisher.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2011 ForgeRock AS.
  */
 package org.opends.server.loggers;
 
@@ -41,7 +42,7 @@
 import org.opends.messages.Message;
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.std.server.AccessLogPublisherCfg;
-import org.opends.server.admin.std.server.FileBasedAccessLogPublisherCfg;
+import org.opends.server.admin.std.server.FileBasedAuditLogPublisherCfg;
 import org.opends.server.api.AccessLogPublisher;
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.AddOperation;
@@ -61,8 +62,8 @@
  * the directory server.
  */
 public class TextAuditLogPublisher extends
-    AccessLogPublisher<FileBasedAccessLogPublisherCfg> implements
-    ConfigurationChangeListener<FileBasedAccessLogPublisherCfg>
+    AccessLogPublisher<FileBasedAuditLogPublisherCfg> implements
+    ConfigurationChangeListener<FileBasedAuditLogPublisherCfg>
 {
 
   private boolean suppressInternalOperations = true;
@@ -71,7 +72,7 @@
 
   private TextWriter writer;
 
-  private FileBasedAccessLogPublisherCfg currentConfig;
+  private FileBasedAuditLogPublisherCfg currentConfig;
 
 
 
@@ -79,7 +80,7 @@
    * {@inheritDoc}
    */
   public ConfigChangeResult applyConfigurationChange(
-      FileBasedAccessLogPublisherCfg config)
+      FileBasedAuditLogPublisherCfg config)
   {
     // Default result code.
     ResultCode resultCode = ResultCode.SUCCESS;
@@ -186,7 +187,7 @@
   public void close()
   {
     writer.shutdown();
-    currentConfig.removeFileBasedAccessChangeListener(this);
+    currentConfig.removeFileBasedAuditChangeListener(this);
   }
 
 
@@ -214,7 +215,7 @@
    */
   @Override()
   public void initializeAccessLogPublisher(
-      FileBasedAccessLogPublisherCfg config)
+      FileBasedAuditLogPublisherCfg config)
       throws ConfigException, InitializationException
   {
     File logFile = getFileForPath(config.getLogFile());
@@ -279,7 +280,7 @@
 
     currentConfig = config;
 
-    config.addFileBasedAccessChangeListener(this);
+    config.addFileBasedAuditChangeListener(this);
   }
 
 
@@ -291,8 +292,8 @@
   public boolean isConfigurationAcceptable(AccessLogPublisherCfg configuration,
       List<Message> unacceptableReasons)
   {
-    FileBasedAccessLogPublisherCfg config =
-      (FileBasedAccessLogPublisherCfg) configuration;
+    FileBasedAuditLogPublisherCfg config =
+      (FileBasedAuditLogPublisherCfg) configuration;
     return isConfigurationChangeAcceptable(config, unacceptableReasons);
   }
 
@@ -302,7 +303,7 @@
    * {@inheritDoc}
    */
   public boolean isConfigurationChangeAcceptable(
-      FileBasedAccessLogPublisherCfg config, List<Message> unacceptableReasons)
+      FileBasedAuditLogPublisherCfg config, List<Message> unacceptableReasons)
   {
     // Make sure the permission is valid.
     try

--
Gitblit v1.10.0