sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1480328 - in /sis/branches/JDK7: sis-metadata/src/main/java/org/apache/sis/internal/metadata/ sis-metadata/src/main/java/org/apache/sis/metadata/iso/ sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/ sis-metadata/src/m...
Date Wed, 08 May 2013 15:44:47 GMT
Author: desruisseaux
Date: Wed May  8 15:44:47 2013
New Revision: 1480328

URL: http://svn.apache.org/r1480328
Log:
Simplified a little bit the API of Logging utility methods,
use a single logger for all org.apache.sis.metadata.iso.* packages,
share the same TestWatcher class in test cases instead than repeating @Before and @After methods.

Added:
    sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/LoggingWatcher.java   (with props)
Removed:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/WarningListeners.java
Modified:
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultResolution.java
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGeorectified.java
    sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java
    sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/identification/DefaultResolutionTest.java
    sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescriptionTest.java
    sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/spatial/DefaultGeorectifiedTest.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/package-info.java
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/TestCase.java

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java [UTF-8] Wed May  8 15:44:47 2013
@@ -17,10 +17,15 @@
 package org.apache.sis.internal.metadata;
 
 import java.util.Date;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
 import org.apache.sis.util.Static;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.resources.Messages;
 import org.apache.sis.metadata.InvalidMetadataException;
 
+import static org.apache.sis.metadata.iso.ISOMetadata.LOGGER;
+
 // Related to JDK7
 import java.util.Objects;
 
@@ -117,4 +122,21 @@ public final class MetadataUtilities ext
         }
         return value;
     }
+
+    /**
+     * Convenience method for logging a warning to the {@code ISOMetadata} logger.
+     * The message will be produced using the {@link Messages} resources bundle.
+     *
+     * @param  caller    The public class which is invoking this method.
+     * @param  method    The public method which is invoking this method.
+     * @param  key       The key from the message resource bundle to use for creating a message.
+     * @param  arguments The arguments to be used together with the key for building the message.
+     */
+    public static void warning(final Class<?> caller, final String method, final int key, final Object... arguments) {
+        final LogRecord record = Messages.getResources(null).getLogRecord(Level.WARNING, key, arguments);
+        record.setSourceClassName(caller.getCanonicalName());
+        record.setSourceMethodName(method);
+        record.setLoggerName(LOGGER.getName());
+        LOGGER.log(record);
+    }
 }

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java [UTF-8] Wed May  8 15:44:47 2013
@@ -17,6 +17,7 @@
 package org.apache.sis.metadata.iso;
 
 import java.util.Collection;
+import java.util.logging.Logger;
 import java.io.Serializable;
 import net.jcip.annotations.ThreadSafe;
 import org.opengis.metadata.Identifier;
@@ -25,6 +26,7 @@ import org.apache.sis.xml.IdentifiedObje
 import org.apache.sis.metadata.MetadataStandard;
 import org.apache.sis.metadata.ModifiableMetadata;
 import org.apache.sis.internal.jaxb.IdentifierMapWithSpecialCases;
+import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.ArgumentChecks;
 
 
@@ -45,6 +47,14 @@ public class ISOMetadata extends Modifia
     private static final long serialVersionUID = -4997239501383133209L;
 
     /**
+     * The logger for warnings emitted by any class in the {@code org.apache.sis.metadata.iso.*} packages.
+     * Warnings are emitted when an action causes the lost of data. For example the {@code "distance"} and
+     * {@code "equivalentScale"} properties in {@link org.apache.sis.metadata.iso.identification.DefaultResolution}
+     * are mutually exclusive: setting one discards the other. In such case, a warning is logged.
+     */
+    public static final Logger LOGGER = Logging.getLogger(ISOMetadata.class);
+
+    /**
      * All identifiers associated with this metadata, or {@code null} if none.
      * This field is initialized to a non-null value when first needed.
      */

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultResolution.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultResolution.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultResolution.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultResolution.java [UTF-8] Wed May  8 15:44:47 2013
@@ -26,7 +26,7 @@ import org.opengis.metadata.identificati
 import org.apache.sis.metadata.iso.ISOMetadata;
 import org.apache.sis.measure.ValueRange;
 import org.apache.sis.util.resources.Messages;
-import org.apache.sis.internal.util.WarningListeners;
+import org.apache.sis.internal.metadata.MetadataUtilities;
 
 
 /**
@@ -127,7 +127,7 @@ public class DefaultResolution extends I
      * Invoked when setting a property discards the other one.
      */
     private static void warning(final String method, final String oldName, final String newName) {
-        WarningListeners.message(null, null, DefaultResolution.class, method,
+        MetadataUtilities.warning(DefaultResolution.class, method,
                 Messages.Keys.DiscardedExclusiveProperty_2, oldName, newName);
     }
 

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescription.java [UTF-8] Wed May  8 15:44:47 2013
@@ -25,7 +25,7 @@ import org.opengis.feature.type.FeatureT
 import org.opengis.metadata.maintenance.ScopeDescription;
 import org.apache.sis.metadata.iso.ISOMetadata;
 import org.apache.sis.internal.metadata.ExcludedSet;
-import org.apache.sis.internal.util.WarningListeners;
+import org.apache.sis.internal.metadata.MetadataUtilities;
 import org.apache.sis.util.collection.CheckedContainer;
 import org.apache.sis.util.resources.Messages;
 
@@ -247,8 +247,8 @@ public class DefaultScopeDescription ext
      */
     private void warningOnOverwrite(final byte code) {
         if (value != null && property != code) {
-                WarningListeners.message(null, null, DefaultScopeDescription.class, SETTERS[code-1],
-                        Messages.Keys.DiscardedExclusiveProperty_2, NAMES[property-1], NAMES[code-1]);
+            MetadataUtilities.warning(DefaultScopeDescription.class, SETTERS[code-1],
+                    Messages.Keys.DiscardedExclusiveProperty_2, NAMES[property-1], NAMES[code-1]);
         }
     }
 

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGeorectified.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGeorectified.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGeorectified.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGeorectified.java [UTF-8] Wed May  8 15:44:47 2013
@@ -27,7 +27,7 @@ import org.opengis.metadata.spatial.Geor
 import org.opengis.metadata.spatial.PixelOrientation;
 import org.opengis.geometry.primitive.Point;
 import org.opengis.util.InternationalString;
-import org.apache.sis.internal.util.WarningListeners;
+import org.apache.sis.internal.metadata.MetadataUtilities;
 import org.apache.sis.util.resources.Messages;
 import org.apache.sis.xml.Namespaces;
 
@@ -199,7 +199,7 @@ public class DefaultGeorectified extends
             booleans |= CHECK_POINT_MASK;
         } else {
             if (checkPointDescription != null && (booleans & CHECK_POINT_MASK) != 0) {
-                WarningListeners.message(null, null, DefaultGeorectified.class, "setCheckPointAvailable",
+                MetadataUtilities.warning(DefaultGeorectified.class, "setCheckPointAvailable",
                         Messages.Keys.PropertyHiddenBy_2, "checkPointDescription", "checkPointAvailability");
             }
             booleans &= ~CHECK_POINT_MASK;

Modified: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/AllMetadataTest.java [UTF-8] Wed May  8 15:44:47 2013
@@ -25,6 +25,8 @@ import org.apache.sis.metadata.MetadataT
 import org.apache.sis.metadata.iso.identification.DefaultRepresentativeFraction;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.xml.Namespaces;
+import org.junit.Rule;
+import org.junit.Test;
 
 import static org.junit.Assert.*;
 
@@ -45,6 +47,14 @@ import static org.junit.Assert.*;
 })
 public final strictfp class AllMetadataTest extends MetadataTestCase {
     /**
+     * A JUnit {@linkplain Rule rule} for listening to log events. This field is public
+     * because JUnit requires us to do so, but should be considered as an implementation
+     * details (it should have been a private field).
+     */
+    @Rule
+    public final LoggingWatcher listener = new LoggingWatcher();
+
+    /**
      * Creates a new test case with all GeoAPI interfaces and code lists to test.
      */
     public AllMetadataTest() {
@@ -196,6 +206,16 @@ public final strictfp class AllMetadataT
     }
 
     /**
+     * Performs the test documente in the {@link MetadataTestCase} javadoc.
+     */
+    @Test
+    @Override
+    public void testPropertyValues() {
+        listener.maximumLogCount = 3;
+        super.testPropertyValues();
+    }
+
+    /**
      * Returns the expected namespace for an element defined by the given specification.
      * For example the namespace of any type defined by {@link Specification#ISO_19115}
      * is {@code "http://www.isotc211.org/2005/gmd"}.

Added: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/LoggingWatcher.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/LoggingWatcher.java?rev=1480328&view=auto
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/LoggingWatcher.java (added)
+++ sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/LoggingWatcher.java [UTF-8] Wed May  8 15:44:47 2013
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.metadata.iso;
+
+import java.util.logging.Filter;
+import java.util.logging.LogRecord;
+import java.util.logging.SimpleFormatter;
+import org.apache.sis.test.TestCase;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Watches the logs sent to {@link ISOMetadata#LOGGER}. Logs will be allowed only if the test
+ * was expected to cause some logging events to occur, otherwise a test failure will occurs.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3
+ * @version 0.3
+ * @module
+ */
+public strictfp class LoggingWatcher extends TestWatcher implements Filter {
+    /**
+     * The maximal number of logs expected by the test. If this value is positive, then it is
+     * decremented when {@link #isLoggable(LogRecord)} is invoked until the value reach zero.
+     * If the value is zero and {@code isLoggable(LogRecord)} is invoked, then a test failure
+     * occurs.
+     *
+     * <p>The initial value of this field is 0. Test cases shall set this field to a non-zero
+     * value in order to allow log events.</p>
+     */
+    public int maximumLogCount;
+
+    /**
+     * The formatter to use for formatting log messages.
+     */
+    private final SimpleFormatter formatter = new SimpleFormatter();
+
+    /**
+     * Creates a new watcher.
+     */
+    public LoggingWatcher() {
+    }
+
+    /**
+     * Invoked when a test is about to start. This method installs this {@link Filter}
+     * for the log messages before the tests are run. This installation will cause the
+     * {@link #isLoggable(LogRecord)} method to be invoked when a message is logged.
+     *
+     * @see #isLoggable(LogRecord)
+     */
+    @Override
+    protected final void starting(final Description description) {
+        assertNull(ISOMetadata.LOGGER.getFilter());
+        ISOMetadata.LOGGER.setFilter(this);
+        maximumLogCount = 0;
+    }
+
+    /**
+     * Invoked when a test method finishes (whether passing or failing)
+     * This method removes the filter which had been set for testing purpose.
+     */
+    @Override
+    protected final void finished(final Description description) {
+        ISOMetadata.LOGGER.setFilter(null);
+    }
+
+    /**
+     * Invoked (indirectly) when a tested method has emitted a log message. This method verifies
+     * if we were expecting a log message, then decrements the {@link #maximumLogCount} value.
+     */
+    @Override
+    public final boolean isLoggable(final LogRecord record) {
+        if (maximumLogCount <= 0) {
+            fail("Unexpected logging:\n" + formatter.format(record));
+        }
+        maximumLogCount--;
+        verifyMessage(formatter.formatMessage(record));
+        return TestCase.verbose;
+    }
+
+    /**
+     * Invoked by {@link #isLoggable(LogRecord)} when a tested method has emitted a log message.
+     * The default implementation does nothing. Subclasses can override this method in order to
+     * perform additional check.
+     *
+     * @param message The logging message.
+     */
+    protected void verifyMessage(final String message) {
+    }
+}

Propchange: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/LoggingWatcher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/LoggingWatcher.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/identification/DefaultResolutionTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/identification/DefaultResolutionTest.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/identification/DefaultResolutionTest.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/identification/DefaultResolutionTest.java [UTF-8] Wed May  8 15:44:47 2013
@@ -16,13 +16,9 @@
  */
 package org.apache.sis.metadata.iso.identification;
 
-import java.util.logging.Filter;
-import java.util.logging.Logger;
-import java.util.logging.LogRecord;
-import org.apache.sis.util.logging.Logging;
+import org.apache.sis.metadata.iso.LoggingWatcher;
 import org.apache.sis.test.TestCase;
-import org.junit.Before;
-import org.junit.After;
+import org.junit.Rule;
 import org.junit.Test;
 
 import static org.apache.sis.test.Assert.*;
@@ -36,37 +32,23 @@ import static org.apache.sis.test.Assert
  * @version 0.3
  * @module
  */
-public final strictfp class DefaultResolutionTest extends TestCase implements Filter {
+public final strictfp class DefaultResolutionTest extends TestCase {
     /**
-     * The logger where to warnings are expected to be sent.
-     */
-    private static final Logger LOGGER = Logging.getLogger(DefaultResolution.class);
-
-    /**
-     * {@code false} when no logging message is expected, and {@code true} when expected.
-     */
-    private boolean isLogExpected;
-
-    /**
-     * Installs this {@link Filter} for the log messages before the tests are run.
-     * This installation will cause the {@link #isLoggable(LogRecord)} method to be
-     * invoked when a message is logged.
-     *
-     * @see #isLoggable(LogRecord)
-     */
-    @Before
-    public void installLogFilter() {
-        assertNull(LOGGER.getFilter());
-        LOGGER.setFilter(this);
-    }
-
-    /**
-     * Removes the filter which has been set for testing purpose.
-     */
-    @After
-    public void removeLogFilter() {
-        LOGGER.setFilter(null);
-    }
+     * A JUnit {@linkplain Rule rule} for listening to log events. This field is public
+     * because JUnit requires us to do so, but should be considered as an implementation
+     * details (it should have been a private field).
+     */
+    @Rule
+    public final LoggingWatcher listener = new LoggingWatcher() {
+        /**
+         * Ensures that the logging message contains the name of the exclusive properties.
+         */
+        @Override
+        protected void verifyMessage(final String message) {
+            assertTrue(message.contains("distance"));
+            assertTrue(message.contains("equivalentScale"));
+        }
+    };
 
     /**
      * Tests the various setter methods. Since they are exclusive properties,
@@ -82,7 +64,7 @@ public final strictfp class DefaultResol
         assertEquals("distance", Double.valueOf(2.0), metadata.getDistance());
         assertNull("equivalentScale", metadata.getEquivalentScale());
 
-        isLogExpected = true;
+        listener.maximumLogCount = 1;
         metadata.setEquivalentScale(scale);
         assertSame("equivalentScale", scale, metadata.getEquivalentScale());
         assertNull("distance", metadata.getDistance());
@@ -95,19 +77,4 @@ public final strictfp class DefaultResol
         assertNull("equivalentScale", metadata.getEquivalentScale());
         assertNull("distance", metadata.getDistance());
     }
-
-    /**
-     * Invoked (indirectly) when a setter method has emitted a warning. This method verifies if we
-     * were expecting a log message, then resets the {@link #isLogExpected} flag to {@code false}
-     * (i.e. we expect at most one logging message).
-     */
-    @Override
-    public boolean isLoggable(final LogRecord record) {
-        final String message = record.getMessage();
-        if (!isLogExpected) {
-            fail("Unexpected logging: " + message);
-        }
-        isLogExpected = false;
-        return verbose;
-    }
 }

Modified: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescriptionTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescriptionTest.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescriptionTest.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/maintenance/DefaultScopeDescriptionTest.java [UTF-8] Wed May  8 15:44:47 2013
@@ -16,13 +16,9 @@
  */
 package org.apache.sis.metadata.iso.maintenance;
 
-import java.util.logging.Filter;
-import java.util.logging.Logger;
-import java.util.logging.LogRecord;
-import org.apache.sis.util.logging.Logging;
+import org.apache.sis.metadata.iso.LoggingWatcher;
 import org.apache.sis.test.TestCase;
-import org.junit.Before;
-import org.junit.After;
+import org.junit.Rule;
 import org.junit.Test;
 
 import static org.apache.sis.test.Assert.*;
@@ -36,37 +32,23 @@ import static org.apache.sis.test.Assert
  * @version 0.3
  * @module
  */
-public final strictfp class DefaultScopeDescriptionTest extends TestCase implements Filter {
+public final strictfp class DefaultScopeDescriptionTest extends TestCase {
     /**
-     * The logger where to warnings are expected to be sent.
-     */
-    private static final Logger LOGGER = Logging.getLogger(DefaultScopeDescription.class);
-
-    /**
-     * {@code false} when no logging message is expected, and {@code true} when expected.
-     */
-    private boolean isLogExpected;
-
-    /**
-     * Installs this {@link Filter} for the log messages before the tests are run.
-     * This installation will cause the {@link #isLoggable(LogRecord)} method to be
-     * invoked when a message is logged.
-     *
-     * @see #isLoggable(LogRecord)
-     */
-    @Before
-    public void installLogFilter() {
-        assertNull(LOGGER.getFilter());
-        LOGGER.setFilter(this);
-    }
-
-    /**
-     * Removes the filter which has been set for testing purpose.
-     */
-    @After
-    public void removeLogFilter() {
-        LOGGER.setFilter(null);
-    }
+     * A JUnit {@linkplain Rule rule} for listening to log events. This field is public
+     * because JUnit requires us to do so, but should be considered as an implementation
+     * details (it should have been a private field).
+     */
+    @Rule
+    public final LoggingWatcher listener = new LoggingWatcher() {
+        /**
+         * Ensures that the logging message contains the name of the exclusive properties.
+         */
+        @Override
+        protected void verifyMessage(final String message) {
+            assertTrue(message.contains("dataset"));
+            assertTrue(message.contains("other"));
+        }
+    };
 
     /**
      * Tests the various setter methods. Since they are exclusive properties,
@@ -78,7 +60,7 @@ public final strictfp class DefaultScope
         metadata.setDataset("A dataset");
         assertEquals("dataset", "A dataset", metadata.getDataset());
 
-        isLogExpected = true;
+        listener.maximumLogCount = 1;
         metadata.setOther("Other value");
         assertEquals("other", "Other value", metadata.getOther());
         assertNull("dataset", metadata.getDataset());
@@ -91,19 +73,4 @@ public final strictfp class DefaultScope
         assertNull("other",   metadata.getOther());
         assertNull("dataset", metadata.getDataset());
     }
-
-    /**
-     * Invoked (indirectly) when a setter method has emitted a warning. This method verifies if we
-     * were expecting a log message, then resets the {@link #isLogExpected} flag to {@code false}
-     * (i.e. we expect at most one logging message).
-     */
-    @Override
-    public boolean isLoggable(final LogRecord record) {
-        final String message = record.getMessage();
-        if (!isLogExpected) {
-            fail("Unexpected logging: " + message);
-        }
-        isLogExpected = false;
-        return verbose;
-    }
 }

Modified: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/spatial/DefaultGeorectifiedTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/spatial/DefaultGeorectifiedTest.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/spatial/DefaultGeorectifiedTest.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/iso/spatial/DefaultGeorectifiedTest.java [UTF-8] Wed May  8 15:44:47 2013
@@ -16,15 +16,11 @@
  */
 package org.apache.sis.metadata.iso.spatial;
 
-import java.util.logging.Filter;
-import java.util.logging.Logger;
-import java.util.logging.LogRecord;
 import org.opengis.util.InternationalString;
-import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.iso.SimpleInternationalString;
+import org.apache.sis.metadata.iso.LoggingWatcher;
 import org.apache.sis.test.TestCase;
-import org.junit.Before;
-import org.junit.After;
+import org.junit.Rule;
 import org.junit.Test;
 
 import static org.apache.sis.test.Assert.*;
@@ -38,37 +34,23 @@ import static org.apache.sis.test.Assert
  * @version 0.3
  * @module
  */
-public final strictfp class DefaultGeorectifiedTest extends TestCase implements Filter {
+public final strictfp class DefaultGeorectifiedTest extends TestCase {
     /**
-     * The logger where to warnings are expected to be sent.
-     */
-    private static final Logger LOGGER = Logging.getLogger(DefaultGeorectified.class);
-
-    /**
-     * {@code false} when no logging message is expected, and {@code true} when expected.
-     */
-    private boolean isLogExpected;
-
-    /**
-     * Installs this {@link Filter} for the log messages before the tests are run.
-     * This installation will cause the {@link #isLoggable(LogRecord)} method to be
-     * invoked when a message is logged.
-     *
-     * @see #isLoggable(LogRecord)
-     */
-    @Before
-    public void installLogFilter() {
-        assertNull(LOGGER.getFilter());
-        LOGGER.setFilter(this);
-    }
-
-    /**
-     * Removes the filter which has been set for testing purpose.
-     */
-    @After
-    public void removeLogFilter() {
-        LOGGER.setFilter(null);
-    }
+     * A JUnit {@linkplain Rule rule} for listening to log events. This field is public
+     * because JUnit requires us to do so, but should be considered as an implementation
+     * details (it should have been a private field).
+     */
+    @Rule
+    public final LoggingWatcher listener = new LoggingWatcher() {
+        /**
+         * Ensures that the logging message contains the name of the exclusive properties.
+         */
+        @Override
+        protected void verifyMessage(final String message) {
+            assertTrue(message.contains("checkPointAvailability"));
+            assertTrue(message.contains("checkPointDescription"));
+        }
+    };
 
     /**
      * Tests {@link DefaultGeorectified#isCheckPointAvailable()} and
@@ -85,7 +67,7 @@ public final strictfp class DefaultGeore
         assertTrue("checkPointAvailability", metadata.isCheckPointAvailable());
 
         // Setting the availability flag shall hide the description and logs a message.
-        isLogExpected = true;
+        listener.maximumLogCount = 1;
         metadata.setCheckPointAvailable(false);
         assertNull("checkPointDescription", metadata.getCheckPointDescription());
 
@@ -93,19 +75,4 @@ public final strictfp class DefaultGeore
         metadata.setCheckPointAvailable(true);
         assertSame("checkPointDescription", description, metadata.getCheckPointDescription());
     }
-
-    /**
-     * Invoked (indirectly) when the {@link DefaultGeorectified#setCheckPointAvailable(boolean)} method
-     * has emitted a warning. This method verifies if we were expecting a log message, then resets the
-     * {@link #isLogExpected} flag to {@code false} (i.e. we expect at most one logging message).
-     */
-    @Override
-    public boolean isLoggable(final LogRecord record) {
-        final String message = record.getMessage();
-        if (!isLogExpected) {
-            fail("Unexpected logging: " + message);
-        }
-        isLogExpected = false;
-        return verbose;
-    }
 }

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java [UTF-8] Wed May  8 15:44:47 2013
@@ -22,10 +22,21 @@ import org.apache.sis.util.collection.We
 
 
 /**
- * A factory for Java {@link Logger} wrapping an other logging framework. This factory is used
- * only when wanting to log to an other framework than JDK logging. The {@link #getLogger(String)}
- * method returns some subclass of {@link Logger} (typically {@link LoggerAdapter}) that forward
- * directly all log methods to an other framework.
+ * A factory for Java {@link Logger} wrapping an other logging framework.
+ * This factory is used only when an application wants to redirect SIS logs to an other framework
+ * than JDK logging. An instance of {@code LoggerFactory} can be registered to SIS in two ways:
+ *
+ * <ul>
+ *   <li>By declaring the fully qualified classname of the {@code LoggerFactory} implementation
+ *       in the {@code META-INF/services/org.apache.sis.util.logging.LoggerFactory} file.
+ *       Note that the {@code sis-logging-commons.jar} and {@code sis-logging-log4j.jar}
+ *       files provide such declaration.</li>
+ *   <li>By explicit invocation of {@link Logging#setLoggerFactory(String, LoggerFactory)}
+ *       at application initialization time.</li>
+ * </ul>
+ *
+ * The {@link #getLogger(String)} method shall return some {@link Logger} subclass
+ * (typically {@link LoggerAdapter}) which forwards directly all log methods to the other framework.
  *
  * @param <L> The type of loggers used for the implementation backend.
  *            This is the type used by external frameworks like Log4J.

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java [UTF-8] Wed May  8 15:44:47 2013
@@ -21,7 +21,6 @@ import java.util.Comparator;
 import java.util.ServiceLoader;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import java.util.logging.Handler;
 import java.util.logging.LogRecord;
 
 import org.apache.sis.util.Configuration;
@@ -33,24 +32,21 @@ import org.apache.sis.util.Classes;
 
 /**
  * A set of utilities method for configuring loggings in SIS. Library implementors shall fetch
- * their logger using the {@link #getLogger(Class)} static method provided in this class rather
- * than {@link Logger#getLogger(String)}, in order to give SIS a chance to redirect the logging
- * to an other framework like <A HREF="http://commons.apache.org/logging/">Commons-logging</A> or
- * <A HREF="http://logging.apache.org/log4j">Log4J</A>.
+ * their loggers using the {@link #getLogger(Class)} static method defined in this {@code Logging}
+ * class rather than the one defined in the standard {@link Logger} class, in order to give SIS a
+ * chance to redirect the logs to an other framework like
+ * <a href="http://commons.apache.org/logging/">Commons-logging</a> or
+ * <a href="http://logging.apache.org/log4j">Log4J</a>.
  *
- * <p>This method provides also a {@link #log(Class, String, LogRecord)} convenience static method,
- * which {@linkplain LogRecord#setLoggerName set the logger name} of the given record before to log
- * it. An other worthy static method is {@link #unexpectedException(Class, String, Throwable)}, for
- * reporting an anomalous but nevertheless non-fatal exception.</p>
- *
- * {@section Configuration}
- * The log records can be redirected explicitly to an other logging framework using the
- * {@link #setLoggerFactory(LoggerFactory)} method.
- * Note however that this method call is performed automatically if the
- * {@code sis-logging-commons.jar} or the {@code sis-logging-log4j.jar} file is
- * found on the classpath, so it usually doesn't need to be invoked explicitely.
- * See the {@link #scanLoggerFactory()} method for more details on automatic logger
- * factory registration.
+ * <p>This class provides also some convenience static methods, including:</p>
+ * <ul>
+ *   <li>{@link #log(Class, String, LogRecord)} for {@linkplain LogRecord#setLoggerName(String) setting
+ *       the logger name}, {@linkplain LogRecord#setSourceClassName(String) source class name} and
+ *       {@linkplain LogRecord#setSourceMethodName(String) source method name} of the given record
+ *       before to log it.</li>
+ *   <li>{@link #unexpectedException(Class, String, Throwable)} for reporting an anomalous but
+ *       nevertheless non-fatal exception.</li>
+ * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-2.4)
@@ -60,6 +56,8 @@ import org.apache.sis.util.Classes;
 public final class Logging extends Static {
     /**
      * Compares {@link Logging} or {@link String} objects for alphabetical order.
+     * Used in order to locate the position where to found an existing element or
+     * insert a new element in the {@link #children} array.
      */
     private static final Comparator<Object> COMPARATOR = new Comparator<Object>() {
         @Override public int compare(final Object o1, final Object o2) {
@@ -70,27 +68,44 @@ public final class Logging extends Stati
     };
 
     /**
-     * An empty array of loggings. Also used for locks.
+     * The initial value of {@link #children} before new loggers are added.
+     * Also opportunistically used for synchronization locks.
      */
     private static final Logging[] EMPTY = new Logging[0];
 
     /**
-     * Logging configuration that apply to all packages.
+     * The root of logging configuration. {@code Logging} scans the classpath for logger factories on class
+     * initialization. The fully qualified factory classname shall be declared in the following file:
+     *
+     * {@preformat text
+     *     META-INF/services/org.apache.sis.util.logging.LoggerFactory
+     * }
+     *
+     * The factory found on the classpath is assigned to the {@link #factory} field. If more than one factory
+     * is found, then the log messages will be sent to the logging frameworks managed by all those factories.
+     *
+     * {@note A previous version was providing a <code>scanForPlugins()</code> method allowing developers to
+     *        refresh the object state when new <code>LoggerFactory</code> instances become available on the
+     *        classpath of a running JVM. However it usually doesn't work since loggers are typically stored
+     *        in static final fields.}
      */
-    public static final Logging ALL = new Logging();
-    static { // Must be run after ALL assignation and before SIS (or any other Logging) creation.
-        ALL.scanLoggerFactory();
+    private static final Logging ROOT = new Logging();
+    static {
+        LoggerFactory<?> factory = null;
+        for (final LoggerFactory<?> found : ServiceLoader.load(LoggerFactory.class)) {
+            if (factory == null) {
+                factory = found;
+            } else {
+                factory = new DualLoggerFactory(factory, found);
+            }
+        }
+        ROOT.factory = factory;
     }
 
     /**
-     * Logging configuration that apply only to "{@code org.apache.sis}" packages.
-     */
-    public static final Logging SIS = getLogging("org.apache.sis");
-
-    /**
-     * The name of the base package.
+     * The name of the logger managed by this {@code Logging} instance.
      */
-    public final String name;
+    final String name;
 
     /**
      * The children {@link Logging} objects.
@@ -118,7 +133,7 @@ public final class Logging extends Stati
 
     /**
      * Creates an instance for the root logger. This constructor should not be used for anything
-     * else than {@link #ALL} construction; use the {@link #getLogging(String)} method instead.
+     * else than {@link #ROOT} construction; use the {@link #getLogging(String)} method instead.
      */
     private Logging() {
         name = "";
@@ -126,7 +141,7 @@ public final class Logging extends Stati
 
     /**
      * Creates an instance for the specified base logger. This constructor should
-     * not be public; use the {@link #getLogging(String)} method instead.
+     * not be public; use the {@link #getLogging(String, boolean)} method instead.
      *
      * @param parent The parent {@code Logging} instance.
      * @param name   The logger name for the new instance.
@@ -138,100 +153,6 @@ public final class Logging extends Stati
     }
 
     /**
-     * Logs the given record to the logger associated to the given class.
-     * This convenience method performs the following steps:
-     *
-     * <ul>
-     *   <li>Get the logger using {@link #getLogger(Class)};</li>
-     *   <li>{@linkplain LogRecord#setLoggerName(String) Set the logger name} of the given record,
-     *       if not already set;</li>
-     *   <li>Unconditionally {@linkplain LogRecord#setSourceClassName(String) set the source class
-     *       name} to the {@linkplain Class#getCanonicalName() canonical name} of the given class;</li>
-     *   <li>Unconditionally {@linkplain LogRecord#setSourceMethodName(String) set the source method
-     *       name} to the given value;</li>
-     *   <li>{@linkplain Logger#log(LogRecord) Log} the modified record.</li>
-     * </ul>
-     *
-     * @param classe The class for which to obtain a logger.
-     * @param method The name of the method which is logging a record.
-     * @param record The record to log.
-     */
-    public static void log(final Class<?> classe, final String method, final LogRecord record) {
-        record.setSourceClassName(classe.getCanonicalName());
-        record.setSourceMethodName(method);
-        final Logger logger = getLogger(classe);
-        if (record.getLoggerName() == null) {
-            record.setLoggerName(logger.getName());
-        }
-        logger.log(record);
-    }
-
-    /**
-     * Returns a logger for the specified class. This convenience method invokes
-     * {@link #getLogger(String)} with the package name as the logger name.
-     *
-     * @param  classe The class for which to obtain a logger.
-     * @return A logger for the specified class.
-     */
-    public static Logger getLogger(Class<?> classe) {
-        Class<?> outer;
-        while ((outer = classe.getEnclosingClass()) != null) {
-            classe = outer;
-        }
-        String name = classe.getName();
-        final int separator = name.lastIndexOf('.');
-        name = (separator >= 1) ? name.substring(0, separator) : "";
-        return getLogger(name);
-    }
-
-    /**
-     * Returns a logger for the specified name. If a {@linkplain LoggerFactory logger factory} has
-     * been set, then this method first {@linkplain LoggerFactory#getLogger ask to the factory}.
-     * It gives SIS a chance to redirect logging events to
-     * <A HREF="http://commons.apache.org/logging/">commons-logging</A>
-     * or some equivalent framework.
-     *
-     * <p>If no factory was found or if the factory choose to not redirect the loggings, then this
-     * method returns the usual <code>{@linkplain Logger#getLogger Logger.getLogger}(name)</code>.</p>
-     *
-     * @param  name The logger name.
-     * @return A logger for the specified name.
-     */
-    public static Logger getLogger(final String name) {
-        synchronized (EMPTY) {
-            final Logging logging = sameLoggerFactory ? ALL : getLogging(name, false);
-            if (logging != null) { // Paranoiac check ('getLogging' should not returns null).
-                final LoggerFactory<?> factory = logging.factory;
-                assert getLogging(name, false).factory == factory : name;
-                if (factory != null) {
-                    final Logger logger = factory.getLogger(name);
-                    if (logger != null) {
-                        return logger;
-                    }
-                }
-            }
-        }
-        return Logger.getLogger(name);
-    }
-
-    /**
-     * Returns a {@code Logging} instance for the specified base logger.
-     * This instance can be used for controlling logging configuration in SIS.
-     *
-     * <p>{@code Logging} instances follow the same hierarchy than {@link Logger}, i.e.
-     * {@code "org.apache.sis"} is the parent of {@code "org.apache.sis.referencing"},
-     * {@code "org.apache.sis.metadata"}, <i>etc</i>.</p>
-     *
-     * @param name The base logger name.
-     * @return The logging instance for the given name.
-     */
-    public static Logging getLogging(final String name) {
-        synchronized (EMPTY) {
-            return getLogging(name, true);
-        }
-    }
-
-    /**
      * Returns a logging instance for the specified base logger. If no instance is found for
      * the specified name and {@code create} is {@code true}, then a new instance will be
      * created. Otherwise the nearest parent is returned.
@@ -242,7 +163,7 @@ public final class Logging extends Stati
      */
     private static Logging getLogging(final String base, final boolean create) {
         assert Thread.holdsLock(EMPTY);
-        Logging logging = ALL;
+        Logging logging = ROOT;
         if (!base.isEmpty()) {
             int offset = 0;
             do {
@@ -269,103 +190,152 @@ public final class Logging extends Stati
     }
 
     /**
-     * Returns the logger factory, or {@code null} if none. This method returns the logger set
-     * by the last call to {@link #setLoggerFactory(LoggerFactory)} on this {@code Logging}
-     * instance or on one of its parent.
+     * Returns {@code true} if all children use the specified factory.
+     * Used in order to detect a possible optimization for this very common case.
+     */
+    private static boolean sameLoggerFactory(final Logging[] children, final LoggerFactory<?> factory) {
+        for (final Logging child : children) {
+            if (child.factory != factory || !sameLoggerFactory(child.children, factory)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Sets a new logger factory for this {@code Logging} instance and every children.
+     * The specified factory will be used by {@link #getLogger(String)} when the given
+     * name is equals to the {@link #name} of this {@code Logging} instance of the name
+     * of one of its parents.
+     *
+     * <p>If the factory is set to {@code null} (the default), then the standard Logging
+     * framework will be used.</p>
      *
-     * @return The current logger factory.
+     * @param factory The new logger factory, or {@code null} if none.
      */
-    public LoggerFactory<?> getLoggerFactory() {
-        synchronized (EMPTY) {
-            return factory;
+    private void setLoggerFactory(final LoggerFactory<?> factory) {
+        this.factory = factory;
+        for (final Logging child : children) {
+            child.setLoggerFactory(factory);
         }
     }
 
     /**
-     * Sets a new logger factory for this {@code Logging} instance and every children. The
-     * specified factory will be used by <code>{@linkplain #getLogger(String) getLogger}(name)</code>
-     * when {@code name} is this {@code Logging} name or one of its children.
+     * Sets a new logger factory to use for the logger of the given name. The specified factory
+     * will be used when the name given to the {@link #getLogger(String)} method is equals to
+     * or is a child of the name given to this method. If the given {@code factory} argument is
+     * {@code null} (the default), then the standard Logging framework will be used for the loggers
+     * of the given name.
+     *
+     * {@section Limitation}
+     * Some SIS classes contain a logger constant declared like below:
      *
-     * <p>If the factory is set to {@code null} (the default), then the standard Logging framework
-     * will be used.</p>
+     * {@preformat java
+     *     public static final Logger LOGGER = Logging.getLogger(TheClass.class);
+     * }
+     *
+     * Factory changes will take effect only if this method is invoked before the initialization
+     * of such classes.
      *
+     * @param name The logger name for which to set the factory.
      * @param factory The new logger factory, or {@code null} if none.
      */
     @Configuration
-    public void setLoggerFactory(final LoggerFactory<?> factory) {
+    public static void setLoggerFactory(final String name, final LoggerFactory<?> factory) {
         synchronized (EMPTY) {
-            this.factory = factory;
-            for (int i=0; i<children.length; i++) {
-                children[i].setLoggerFactory(factory);
-            }
-            sameLoggerFactory = sameLoggerFactory(ALL.children, ALL.factory); // NOSONAR: really want static field.
+            getLogging(name, true).setLoggerFactory(factory);
+            sameLoggerFactory = sameLoggerFactory(ROOT.children, ROOT.factory); // Really want static fields.
         }
     }
 
     /**
-     * Returns {@code true} if all children use the specified factory.
-     * Used in order to detect a possible optimization for this very common case.
+     * Returns the factory used for the loggers of the given name, or {@code null} if none. This method
+     * returns the factory set by the last call to {@link #setLoggerFactory(String, LoggerFactory)} for
+     * the given name or for a parent of the given name.
+     *
+     * @param name The logger name for which to get the factory.
+     * @return The current logger factory for the given name or for a parent, or {@code null} if none.
      */
-    private static boolean sameLoggerFactory(final Logging[] children, final LoggerFactory<?> factory) {
-        assert Thread.holdsLock(EMPTY);
-        for (int i=0; i<children.length; i++) {
-            final Logging logging = children[i];
-            if (logging.factory != factory || !sameLoggerFactory(logging.children, factory)) {
-                return false;
-            }
+    public static LoggerFactory<?> getLoggerFactory(final String name) {
+        synchronized (EMPTY) {
+            final Logging logging = getLogging(name, false);
+            return (logging != null) ? logging.factory : null;
         }
-        return true;
     }
 
     /**
-     * Scans the classpath for logger factories. The fully qualified factory classname shall be
-     * declared in the following file:
-     *
-     * {@preformat text
-     *     META-INF/services/org.apache.sis.util.logging.LoggerFactory
-     * }
+     * Returns a logger for the specified name. If a {@linkplain LoggerFactory logger factory} has
+     * been set, then this method first {@linkplain LoggerFactory#getLogger asks to the factory}.
+     * This rule gives SIS a chance to redirect logging events to
+     * <a href="http://commons.apache.org/logging/">commons-logging</a> or some equivalent framework.
+     * Only if no factory was found or if the factory choose to not redirect the loggings, then this
+     * method delegate to <code>{@linkplain Logger#getLogger Logger.getLogger}(name)</code>.
      *
-     * The factory found on the classpath is given to {@link #setLoggerFactory(LoggerFactory)}.
-     * If more than one factory is found, then the log messages will be sent to the logging
-     * frameworks managed by all those factories.
-     *
-     * <p>This method usually doesn't need to be invoked explicitly, since it is automatically
-     * invoked on {@code Logging} class initialization. However developers may invoke it if
-     * new {@code LoggerFactory}s are added later on the classpath of a running JVM.</p>
+     * @param  name The logger name.
+     * @return A logger for the specified name.
      */
-    @Configuration
-    public void scanLoggerFactory() {
-        LoggerFactory<?> factory = null;
-        for (final LoggerFactory<?> found : ServiceLoader.load(LoggerFactory.class)) {
-            if (factory == null) {
-                factory = found;
-            } else {
-                factory = new DualLoggerFactory(factory, found);
+    public static Logger getLogger(final String name) {
+        synchronized (EMPTY) {
+            final Logging logging = sameLoggerFactory ? ROOT : getLogging(name, false);
+            if (logging != null) { // Paranoiac check ('getLogging' should not returns null).
+                final LoggerFactory<?> factory = logging.factory;
+                assert getLogging(name, false).factory == factory : name;
+                if (factory != null) {
+                    final Logger logger = factory.getLogger(name);
+                    if (logger != null) {
+                        return logger;
+                    }
+                }
             }
         }
-        setLoggerFactory(factory);
+        return Logger.getLogger(name);
+    }
+
+    /**
+     * Returns a logger for the specified class. This convenience method invokes
+     * {@link #getLogger(String)} with the package name as the logger name.
+     *
+     * @param  classe The class for which to obtain a logger.
+     * @return A logger for the specified class.
+     */
+    public static Logger getLogger(Class<?> classe) {
+        Class<?> outer;
+        while ((outer = classe.getEnclosingClass()) != null) {
+            classe = outer;
+        }
+        String name = classe.getName();
+        final int separator = name.lastIndexOf('.');
+        name = (separator >= 1) ? name.substring(0, separator) : "";
+        return getLogger(name);
     }
 
     /**
-     * Flushes all {@linkplain Handler handlers} used by the logger named {@link #name}.
-     * If that logger {@linkplain Logger#getUseParentHandlers() uses parent handlers},
-     * then those handlers will be flushed as well.
+     * Logs the given record to the logger associated to the given class.
+     * This convenience method performs the following steps:
      *
-     * <p>If the log records seem to be interleaved with the content of {@link System#out}
-     * or {@link System#err}, invoking this method before to write to the standard streams
-     * may help.</p>
+     * <ul>
+     *   <li>Get the logger using {@link #getLogger(Class)};</li>
+     *   <li>{@linkplain LogRecord#setLoggerName(String) Set the logger name} of the given record,
+     *       if not already set;</li>
+     *   <li>Unconditionally {@linkplain LogRecord#setSourceClassName(String) set the source class
+     *       name} to the {@linkplain Class#getCanonicalName() canonical name} of the given class;</li>
+     *   <li>Unconditionally {@linkplain LogRecord#setSourceMethodName(String) set the source method
+     *       name} to the given value;</li>
+     *   <li>{@linkplain Logger#log(LogRecord) Log} the modified record.</li>
+     * </ul>
      *
-     * @see Handler#flush()
+     * @param classe The class for which to obtain a logger.
+     * @param method The name of the method which is logging a record.
+     * @param record The record to log.
      */
-    public void flush() {
-        for (Logger logger=getLogger(name); logger!=null; logger=logger.getParent()) {
-            for (final Handler handler : logger.getHandlers()) {
-                handler.flush();
-            }
-            if (!logger.getUseParentHandlers()) {
-                break;
-            }
+    public static void log(final Class<?> classe, final String method, final LogRecord record) {
+        record.setSourceClassName(classe.getCanonicalName());
+        record.setSourceMethodName(method);
+        final Logger logger = getLogger(classe);
+        if (record.getLoggerName() == null) {
+            record.setLoggerName(logger.getName());
         }
+        logger.log(record);
     }
 
     /**

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/package-info.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/package-info.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/logging/package-info.java [UTF-8] Wed May  8 15:44:47 2013
@@ -33,19 +33,25 @@
  * The SIS project uses the standard {@link java.util.logging.Logger} API for its logging,
  * but this package allows redirection of logging messages to some other frameworks like
  * <a href="http://logging.apache.org/log4j/">Log4J</a>.
- *
- * <p>We recommend to stick to standard JDK logging when possible. However if inter-operability
+ * We recommend to stick to standard JDK logging when possible. However if inter-operability
  * with an other logging framework is required, then the only action needed is to include
- * <strong>one</strong> of the following JAR on the classpath:</p>
+ * <strong>one</strong> of the following JAR on the classpath:
+ *
+ * <ul>
+ *   <li>{@code sis-logging-commons.jar} for Apache logging</li>
+ *   <li>{@code sis-logging-log4j.jar} for Log4J logging</li>
+ *   <li>Any other JAR registering a {@link org.apache.sis.util.logging.LoggerFactory} implementation.</li>
+ * </ul>
  *
+ * {@section Apache SIS logger constants}
+ * Some static final {@code Logger} constant defined in Apache SIS are:
  * <ul>
- *   <li>{@code geotk-logging-commons.jar} for Apache logging</li>
- *   <li>{@code geotk-logging-log4j.jar} for Log4J logging</li>
+ *   <li>{@link org.apache.sis.metadata.iso.ISOMetadata#LOGGER} for the {@code org.apache.sis.metadata.iso.*} packages</li>
  * </ul>
  *
  * {@section Note for SIS developers}
  * All SIS code should fetch their logger through a call to our custom
- * {@link org.apache.sis.util.logging.Logging#getLogger(String)} method, <strong>not</strong>
+ * {@link org.apache.sis.util.logging.Logging#getLogger(String)} method instead than
  * the standard {@link java.util.logging.Logger#getLogger(String)} method. This is necessary in
  * order to give SIS a chance to redirect log events to an other logging framework.
  *

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/TestCase.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/TestCase.java?rev=1480328&r1=1480327&r2=1480328&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/TestCase.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/TestCase.java [UTF-8] Wed May  8 15:44:47 2013
@@ -92,7 +92,7 @@ public abstract strictfp class TestCase 
      * {@code true} if the {@value org.apache.sis.test.TestConfiguration#VERBOSE_OUTPUT_KEY}
      * system property is set to {@code true}.
      */
-    protected static final boolean verbose;
+    public static final boolean verbose;
 
     /**
      * Sets the {@link #out} writer and its underlying {@link #buffer}.



Mime
View raw message