Author: desruisseaux Date: Thu May 30 16:17:57 2013 New Revision: 1487913 URL: http://svn.apache.org/r1487913 Log: Provides a WarningListener interface.
Added: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java (with props) sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java (with props) sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningProducer.java - copied, changed from r1487611, sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/WarningProducer.java Removed: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/WarningProducer.java Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java Added: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java?rev=1487913&view=auto ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java (added) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java [UTF-8] Thu May 30 16:17:57 2013 @@ -0,0 +1,65 @@ +/* + * 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.util.logging; + +import java.util.EventListener; +import java.util.logging.Level; +import java.util.logging.LogRecord; + + +/** + * Intercepts non-fatal error messages logged by {@link org.apache.sis.storage.DataStore} or other SIS objects. + * Warnings are encapsulated in {@link LogRecord} objects and logged at {@link Level#WARNING} if the emitter does not + * have any registered any {@code WarningListener}. This listener allows applications to intercept warning records for: + * + * <ul> + * <li>displaying the warning in a dialog or performing any other action that the application may choose,</li> + * <li>reducing the amount of records to be logged.</li> + * </ul> + * + * The difference between using this listener or configuring the logging {@link java.util.logging.Handler} is + * that listeners allow to handle the warnings on a per-{@code DataStore} (or any other emitter) instance. + * + * @param <T> The type of the warnings emitter. + * + * @author Martin Desruisseaux (Geomatys) + * @since 0.3 + * @version 0.3 + * @module + * + * @see org.apache.sis.storage.DataStore#addWarningListener(WarningListener) + */ +public interface WarningListener<T> extends EventListener { + /** + * Reports the occurrence of a non-fatal error. The emitter process (often a + * {@link org.apache.sis.storage.DataStore} in the midst of a reading process) + * will continue following the call to this method. + * + * <p>The {@code LogRecord} provides the warning {@linkplain LogRecord#getMessage() message} together with + * programmatic information like the {@linkplain LogRecord#getSourceClassName() source class name} and + * {@linkplain LogRecord#getSourceMethodName() method name} where the warning occurred. The log record + * may optionally contains the exception which has been {@linkplain LogRecord#getThrown() thrown}.</p> + * + * <p>Applications may choose to ignore the warning, display a dialog or take any other action they choose. + * Applications do not need to log the warning, since logging will be done automatically if the emitter has + * no registered warning listeners.</p> + * + * @param source The object that emitted a warning. + * @param warning The warning message together with programmatic information. + */ + void warningOccured(T source, LogRecord warning); +} Propchange: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java ------------------------------------------------------------------------------ svn:mime-type = text/plain;charset=UTF-8 Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1487913&r1=1487912&r2=1487913&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] Thu May 30 16:17:57 2013 @@ -311,6 +311,11 @@ public final class Errors extends Indexe public static final int NegativeArrayLength_1 = 78; /** + * Element “{0}” has not been found. + */ + public static final int NoSuchElement_1 = 96; + + /** * No property named “{0}” has been found in “{1}”. */ public static final int NoSuchProperty_2 = 73; Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1487913&r1=1487912&r2=1487913&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] (original) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] Thu May 30 16:17:57 2013 @@ -88,6 +88,7 @@ NonTemporalUnit_1 = \u201c NotANumber_1 = Argument \u2018{0}\u2019 shall not be NaN (Not-a-Number). NotAPrimitiveWrapper_1 = Class \u2018{0}\u2019 is not a primitive type wrapper. NotComparableClass_1 = Class \u2018{0}\u2019 is not a comparable. +NoSuchElement_1 = Element \u201c{0}\u201d has not been found. NoSuchProperty_2 = No property named \u201c{0}\u201d has been found in \u201c{1}\u201d. NoUnit = No unit of measurement has been specified. NullArgument_1 = Argument \u2018{0}\u2019 shall not be null. Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1487913&r1=1487912&r2=1487913&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] (original) +++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] Thu May 30 16:17:57 2013 @@ -78,6 +78,7 @@ NonTemporalUnit_1 = \u201c NotANumber_1 = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre NaN (Not-a-Number). NotAPrimitiveWrapper_1 = La classe \u2018{0}\u2019 n\u2019est pas un adaptateur d\u2019un type primitif. NotComparableClass_1 = La classe \u2018{0}\u2019 n\u2019est pas comparable. +NoSuchElement_1 = L\u2019\u00e9lement \u201c{0}\u201d n\u2019a pas \u00e9t\u00e9 trouv\u00e9. NoSuchProperty_2 = Aucune propri\u00e9t\u00e9 nomm\u00e9e \u201c{0}\u201d n\u2019a \u00e9t\u00e9 trouv\u00e9e dans \u201c{1}\u201d. NoUnit = Aucune unit\u00e9 de mesure n\u2019a \u00e9t\u00e9 sp\u00e9cifi\u00e9e. NullArgument_1 = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre nul. Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java?rev=1487913&r1=1487912&r2=1487913&view=diff ============================================================================== --- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java [UTF-8] (original) +++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java [UTF-8] Thu May 30 16:17:57 2013 @@ -21,6 +21,7 @@ import java.io.Closeable; import java.io.IOException; import javax.measure.unit.Unit; import org.apache.sis.measure.Units; +import org.apache.sis.internal.storage.WarningProducer; /** @@ -33,12 +34,18 @@ import org.apache.sis.measure.Units; */ public abstract class Decoder extends WarningProducer implements Closeable { /** + * Sets to {@code true} for canceling a reading process. + * This flag is honored on a <cite>best effort</cite> basis only. + */ + public volatile boolean canceled; + + /** * Creates a new decoder. * - * @param parent Where to send the warnings, or {@code null} if none. + * @param sink Where to send the warnings, or {@code null} if none. */ - protected Decoder(final WarningProducer parent) { - super(parent); + protected Decoder(final WarningProducer sink) { + super(sink); } /** Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java?rev=1487913&r1=1487912&r2=1487913&view=diff ============================================================================== --- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java [UTF-8] (original) +++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java [UTF-8] Thu May 30 16:17:57 2013 @@ -37,7 +37,7 @@ import org.apache.sis.internal.jdk8.Func import org.apache.sis.internal.netcdf.Decoder; import org.apache.sis.internal.netcdf.Variable; import org.apache.sis.internal.netcdf.GridGeometry; -import org.apache.sis.internal.netcdf.WarningProducer; +import org.apache.sis.internal.storage.WarningProducer; import org.apache.sis.internal.storage.ChannelDataInput; import org.apache.sis.internal.util.CollectionsExt; import org.apache.sis.internal.jdk8.JDK8; @@ -173,16 +173,16 @@ public final class ChannelDecoder extend * Creates a new decoder for the given file. * This constructor parses immediately the header. * - * @param parent Where to send the warnings, or {@code null} if none. + * @param sink Where to send the warnings, or {@code null} if none. * @param filename A file identifier used only for formatting error message. * @param channel The channel from where data are read. * @throws IOException If an error occurred while reading the channel. * @throws DataStoreException If the content of the given channel is not a NetCDF file. */ - public ChannelDecoder(final WarningProducer parent, final String filename, final ReadableByteChannel channel) + public ChannelDecoder(final WarningProducer sink, final String filename, final ReadableByteChannel channel) throws IOException, DataStoreException { - super(parent); + super(sink); // The buffer must be backed by a Java {@code byte[]} array, // because we will occasionally reference that array. input = new ChannelDataInput(filename, channel, ByteBuffer.allocate(4096), false); Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java?rev=1487913&r1=1487912&r2=1487913&view=diff ============================================================================== --- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java [UTF-8] (original) +++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java [UTF-8] Thu May 30 16:17:57 2013 @@ -21,11 +21,13 @@ import java.util.List; import java.util.EnumSet; import java.io.IOException; import ucar.nc2.Group; +import ucar.nc2.Dimension; import ucar.nc2.Attribute; import ucar.nc2.VariableIF; import ucar.nc2.NetcdfFile; import ucar.nc2.dataset.NetcdfDataset; import ucar.nc2.dataset.CoordinateSystem; +import ucar.nc2.util.CancelTask; import ucar.nc2.units.DateUnit; import ucar.nc2.time.Calendar; import ucar.nc2.time.CalendarDate; @@ -34,8 +36,7 @@ import org.apache.sis.util.ArraysExt; import org.apache.sis.internal.netcdf.Decoder; import org.apache.sis.internal.netcdf.Variable; import org.apache.sis.internal.netcdf.GridGeometry; -import org.apache.sis.internal.netcdf.WarningProducer; -import ucar.nc2.Dimension; +import org.apache.sis.internal.storage.WarningProducer; /** @@ -46,7 +47,7 @@ import ucar.nc2.Dimension; * @version 0.3 * @module */ -public final class DecoderWrapper extends Decoder { +public final class DecoderWrapper extends Decoder implements CancelTask { /** * The NetCDF file to read. * This file is set at construction time. @@ -85,15 +86,27 @@ public final class DecoderWrapper extend * {@link NetcdfFile} instance, the {@link NetcdfDataset} subclass is necessary in order to * get coordinate system information. * - * @param parent Where to send the warnings, or {@code null} if none. - * @param file The NetCDF file from which to parse metadata. + * @param sink Where to send the warnings, or {@code null} if none. + * @param file The NetCDF file from which to read data. */ - public DecoderWrapper(final WarningProducer parent, final NetcdfFile file) { - super(parent); + public DecoderWrapper(final WarningProducer sink, final NetcdfFile file) { + super(sink); this.file = file; } /** + * Creates a new decoder for the given filename. + * + * @param sink Where to send the warnings, or {@code null} if none. + * @param filename The name of the NetCDF file from which to read data. + * @throws IOException If an error occurred while opening the NetCDF file. + */ + public DecoderWrapper(final WarningProducer sink, final String filename) throws IOException { + super(sink); + file = NetcdfDataset.openDataset(filename, false, this); + } + + /** * Defines the groups where to search for named attributes, in preference order. * The {@code null} group name stands for the global attributes. */ @@ -311,6 +324,27 @@ public final class DecoderWrapper extend } /** + * Invoked by the UCAR NetCDF library for checking if the reading process has been canceled. + * This method returns the {@link #canceled} flag. + * + * @return The {@link #canceled} flag. + */ + @Override + public boolean isCancel() { + return canceled; + } + + /** + * Invoked by the UCAR NetCDF library when an error occurred. + * + * @param message The error message. + */ + @Override + public void setError(final String message) { + warning(null, message); + } + + /** * Closes the NetCDF file. * * @throws IOException If an error occurred while closing the file. Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java?rev=1487913&r1=1487912&r2=1487913&view=diff ============================================================================== --- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java [UTF-8] (original) +++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java [UTF-8] Thu May 30 16:17:57 2013 @@ -64,7 +64,7 @@ import org.apache.sis.internal.netcdf.Ax import org.apache.sis.internal.netcdf.Decoder; import org.apache.sis.internal.netcdf.Variable; import org.apache.sis.internal.netcdf.GridGeometry; -import org.apache.sis.internal.netcdf.WarningProducer; +import org.apache.sis.internal.storage.WarningProducer; import org.apache.sis.internal.util.DefaultFactories; import org.apache.sis.internal.metadata.MetadataUtilities; @@ -169,12 +169,11 @@ final class MetadataReader extends Warni /** * Creates a new <cite>NetCDF to ISO</cite> mapper for the given source. * - * @param parent Where to send the warnings, or {@code null} if none. * @param decoder The source of NetCDF attributes. * @throws IOException If an I/O operation was necessary but failed. */ - MetadataReader(final WarningProducer parent, final Decoder decoder) throws IOException { - super(parent); + MetadataReader(final Decoder decoder) throws IOException { + super(decoder.sink); this.decoder = decoder; decoder.setSearchPath(SEARCH_PATH); searchPath = decoder.getSearchPath(); Modified: sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java?rev=1487913&r1=1487912&r2=1487913&view=diff ============================================================================== --- sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java [UTF-8] (original) +++ sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java [UTF-8] Thu May 30 16:17:57 2013 @@ -66,7 +66,7 @@ public final strictfp class ConformanceT @Override protected Metadata wrap(final NetcdfFile file) throws IOException { final Decoder decoder = new DecoderWrapper(null, file); - final MetadataReader ncISO = new MetadataReader(null, decoder); + final MetadataReader ncISO = new MetadataReader(decoder); return ncISO.read(); // Do not close the file, as this will be done by the parent test class. } Modified: sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java?rev=1487913&r1=1487912&r2=1487913&view=diff ============================================================================== --- sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java [UTF-8] (original) +++ sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java [UTF-8] Thu May 30 16:17:57 2013 @@ -53,7 +53,7 @@ public final strictfp class MetadataRead public void testEmbedded() throws IOException { final Metadata metadata; try (Decoder input = ChannelDecoderTest.createChannelDecoder(NCEP)) { - metadata = new MetadataReader(null, input).read(); + metadata = new MetadataReader(input).read(); } compareToExpected(metadata); } @@ -68,7 +68,7 @@ public final strictfp class MetadataRead public void testUCAR() throws IOException { final Metadata metadata; try (Decoder input = new DecoderWrapper(null, new NetcdfDataset(open(NCEP)))) { - metadata = new MetadataReader(null, input).read(); + metadata = new MetadataReader(input).read(); } compareToExpected(metadata); } Added: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java?rev=1487913&view=auto ============================================================================== --- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java (added) +++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java [UTF-8] Thu May 30 16:17:57 2013 @@ -0,0 +1,136 @@ +/* + * 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.internal.storage; + +import java.util.logging.Logger; +import java.util.logging.LogRecord; +import java.util.NoSuchElementException; +import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.resources.Errors; +import org.apache.sis.util.logging.WarningListener; + + +/** + * The leaf of a chain of {@link WarningProducer}, which hold the list of {@link WarningListener}s to notify. + * + * @param <T> The type of the object declared as warnings emitter. + * + * @author Martin Desruisseaux (Geomatys) + * @since 0.3 + * @version 0.3 + * @module + */ +public final class WarningConsumer<T> extends WarningProducer { + /** + * The declared source of warnings. This is not necessarily the real source, + * but this is the source that we declare in public API. + */ + private final T source; + + /** + * Where to log the warnings when there is no registered listener. + */ + private final Logger logger; + + /** + * The listeners, or {@code null} if none. This is a <cite>copy on write</cite> array: + * no elements are modified once the array have been created. + */ + private volatile WarningListener<? super T>[] listeners; + + /** + * Creates a new instance with initially no listener. + * Warnings will be logger to the given logger, unless at least one listener is registered. + * + * @param source The declared source of warnings. This is not necessarily the real source, + * but this is the source that we declare in public API. + * @param logger Where to log the warnings when there is no registered listener. + */ + public WarningConsumer(final T source, final Logger logger) { + super(null); + this.source = source; + this.logger = logger; + } + + /** + * Invoked when a new warning has been emitted. This method notifies the listeners if any, + * or log the warning otherwise. + */ + @Override + void sendWarning(final LogRecord record) { + final WarningListener[] current = listeners; + if (current != null) { + for (final WarningListener<? super T> listener : listeners) { + listener.warningOccured(source, record); + } + } else { + record.setLoggerName(logger.getName()); + logger.log(record); + } + } + + /** + * Adds a listener to be notified when a warning occurred while reading from or writing to the storage. + * + * @param listener The listener to add. + * @throws IllegalArgumentException If the given listener is already registered in this data store. + */ + public void addWarningListener(final WarningListener<? super T> listener) throws IllegalArgumentException { + ArgumentChecks.ensureNonNull("listener", listener); + final WarningListener<? super T>[] current = listeners; + final int length = (current != null) ? current.length : 0; + + @SuppressWarnings({"unchecked", "rawtypes"}) // Generic array creation. + final WarningListener<? super T>[] copy = new WarningListener[length + 1]; + for (int i=0; i<length; i++) { + final WarningListener<? super T> c = current[i]; + if (c == listener) { + throw new IllegalArgumentException(Errors.format(Errors.Keys.ElementAlreadyPresent_1, listener)); + } + copy[i] = c; + } + copy[length] = listener; + listeners = copy; + } + + /** + * Removes a previously registered listener. + * + * @param listener The listener to remove. + * @throws NoSuchElementException If the given listener is not registered in this data store. + */ + public void removeWarningListener(final WarningListener<? super T> listener) throws NoSuchElementException { + final WarningListener<? super T>[] current = listeners; + if (current != null) { + for (int i=0; i<current.length; i++) { + if (current[i] == listener) { + if (current.length == 1) { + listeners = null; + } else { + @SuppressWarnings({"unchecked", "rawtypes"}) // Generic array creation. + final WarningListener<? super T>[] copy = new WarningListener[current.length - 1]; + System.arraycopy(current, 0, copy, 0, i); + System.arraycopy(current, i+1, copy, i, copy.length - i); + listeners = copy; + } + return; + } + } + } + throw new NoSuchElementException(Errors.format(Errors.Keys.NoSuchElement_1, listener)); + } +} Propchange: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java ------------------------------------------------------------------------------ svn:mime-type = text/plain;charset=UTF-8 Copied: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningProducer.java (from r1487611, sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/WarningProducer.java) URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningProducer.java?p2=sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningProducer.java&p1=sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/WarningProducer.java&r1=1487611&r2=1487913&rev=1487913&view=diff ============================================================================== --- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/WarningProducer.java [UTF-8] (original) +++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningProducer.java [UTF-8] Thu May 30 16:17:57 2013 @@ -14,17 +14,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.sis.internal.netcdf; +package org.apache.sis.internal.storage; import java.util.logging.Level; -import java.util.logging.Logger; import java.util.logging.LogRecord; import org.apache.sis.util.Exceptions; import org.apache.sis.util.logging.Logging; +import org.apache.sis.util.logging.WarningListener; /** - * Base class of NetCDF classes which may produce warnings. + * Base class of storage classes which may produce warnings. Warnings are emitted by invoking one of the + * {@code warning(…)} methods and encapsulated in {@link LogRecord} instances. All warnings ultimately + * go through the {@link #sendWarning(LogRecord)} method, thus providing a single method to override if + * some additional handling is needed. When a warning is emitted, there is a choice: + * + * <ul> + * <li>If this {@code WarningProducer} is part of a larger process represented by an other {@code WarningProducer} + * instance (i.e. if this instance has a non-null {@link #sink}), then the {@link #sendWarning(LogRecord)} + * method will delegate its work to that other {@code WarningProducer} instance (the sink).</li> + * + * <li>Otherwise, if there is any {@link WarningListener}, then those listeners are notified and the warning is + * <strong>not</strong> logged. This case is actually implemented by the {@link WarningConsumer} subclass.</li> + * + * <li>Otherwise the warning is logged to the logger given to the {@link WarningConsumer} constructor.</li> + * </ul> * * @author Martin Desruisseaux (Geomatys) * @since 0.3 (derived from geotk-3.08) @@ -33,45 +47,56 @@ import org.apache.sis.util.logging.Loggi */ public class WarningProducer { /** - * Where to send the warnings, or {@code null} if none. + * Where to send the warnings, or {@code null} if none. This field is always {@code null} for + * {@link WarningConsumer}, since the later will redirect warnings to the listeners (if any). */ - private final WarningProducer parent; + public final WarningProducer sink; /** - * Creates a new instance. + * Creates a new instance which will send the warnings to the given object. * - * @param parent Where to send the warnings, or {@code null} if none. + * @param sink Where to send the warnings, or {@code null} if none. */ - protected WarningProducer(final WarningProducer parent) { - this.parent = parent; + protected WarningProducer(final WarningProducer sink) { + this.sink = sink; } /** - * Reports a warning represented by the given log record. The current implementation just logs the warning. - * However if we want to implement a listener mechanism in a future version, this could be done here. + * Reports a warning represented by the given log record. The default implementation delegates to the + * {@link #sink} if any, or logs the message to a default logger otherwise. The {@link WarningConsumer} + * subclass overrides this method in order to notify listeners or use a different logger. * * @param record The warning as a log record. */ - private void warning(final LogRecord record) { - if (parent != null) { - parent.warning(record); + void sendWarning(final LogRecord record) { + if (sink != null) { + sink.sendWarning(record); } else { - final Logger logger = Logging.getLogger(WarningProducer.class); - record.setLoggerName(logger.getName()); - logger.log(record); + record.setLoggerName("org.apache.sis.storage"); + Logging.getLogger("org.apache.sis.storage").log(record); } } /** + * Reports a warning represented by the given message. + * + * @param methodName The name of the method in which the warning occurred. + * @param message The message to log. + */ + protected final void warning(final String methodName, final String message) { + final LogRecord record = new LogRecord(Level.WARNING, message); + record.setSourceClassName(getClass().getCanonicalName()); + record.setSourceMethodName(methodName); + sendWarning(record); + } + + /** * Reports a warning represented by the given exception. * * @param methodName The name of the method in which the warning occurred. * @param exception The exception to log. */ protected final void warning(final String methodName, final Exception exception) { - final LogRecord record = new LogRecord(Level.WARNING, Exceptions.formatChainedMessages(null, null, exception)); - record.setSourceClassName(getClass().getCanonicalName()); - record.setSourceMethodName(methodName); - warning(record); + warning(methodName, Exceptions.formatChainedMessages(null, null, exception)); } } Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java?rev=1487913&r1=1487912&r2=1487913&view=diff ============================================================================== --- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java [UTF-8] (original) +++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java [UTF-8] Thu May 30 16:17:57 2013 @@ -16,7 +16,9 @@ */ package org.apache.sis.storage; +import java.util.NoSuchElementException; import org.opengis.metadata.Metadata; +import org.apache.sis.util.logging.WarningListener; /** @@ -40,6 +42,42 @@ public interface DataStore extends AutoC Metadata getMetadata() throws DataStoreException; /** + * Adds a listener to be notified when a warning occurred while reading from or writing to the storage. + * When a warning occurs, there is a choice: + * + * <ul> + * <li>If this data store has no warning listener, then the warning is logged at + * {@link java.util.logging.Level#WARNING}.</li> + * <li>If this data store has at least one warning listener, then all listeners are notified + * and the warning is <strong>not</strong> logged by this data store instance.</li> + * </ul> + * + * Consider invoking this method in a {@code try} … {@code finally} block if the {@code DataStore} + * lifetime is longer than the listener lifetime, as below: + * + * {@preformat java + * datastore.addWarningListener(listener); + * try { + * // Do some work... + * } finally { + * datastore.removeWarningListener(listener); + * } + * } + * + * @param listener The listener to add. + * @throws IllegalArgumentException If the given listener is already registered in this data store. + */ + void addWarningListener(WarningListener<? super DataStore> listener) throws IllegalArgumentException; + + /** + * Removes a previously registered listener. + * + * @param listener The listener to remove. + * @throws NoSuchElementException If the given listener is not registered in this data store. + */ + void removeWarningListener(WarningListener<? super DataStore> listener) throws NoSuchElementException; + + /** * Closes this data store and releases any underlying resources. * * @throws DataStoreException If an error occurred while closing this data store.