Perform argument checks and throw {@link IllegalArgumentException} if needed.
*
{@link org.apache.sis.util.Exceptions}
*
Format a stack trace summary or change the exception message.
+ *
{@link org.apache.sis.util.logging.Logging}
+ *
Get a JDK {@linkplain java.util.logging.Logger logger}, which may be a wrapper around
+ * the Apache Commons Logging or Log4J framework.
*
*
* @author Martin Desruisseaux (Geomatys)
Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java?rev=1387950&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java Thu Sep 20 10:35:57 2012
@@ -0,0 +1,101 @@
+/*
+ * 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.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Redirects logging to two loggers. This is used only when more than one {@link LoggerFactory}
+ * is found on the classpath. This should never happen, but if it happen anyway we will send the
+ * log records to all registered loggers in order to have a behavior slightly more determinist
+ * than picking an arbitrary logger.
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ * @since 0.3 (derived from geotk-3.20)
+ * @version 0.3
+ * @module
+ */
+final class DualLogger extends LoggerAdapter {
+ /**
+ * The two loggers.
+ */
+ private final Logger first, second;
+
+ /**
+ * Creates a new logger which will redirects the event to the two specified loggers.
+ */
+ DualLogger(final String name, final Logger first, final Logger second) {
+ super(name);
+ this.first = first;
+ this.second = second;
+ }
+
+ /**
+ * Sets the level for the two loggers.
+ */
+ @Override
+ public void setLevel(final Level level) {
+ first .setLevel(level);
+ second.setLevel(level);
+ }
+
+ /**
+ * Returns the finest level from the two loggers.
+ */
+ @Override
+ public Level getLevel() {
+ final Level v1 = first .getLevel();
+ final Level v2 = second.getLevel();
+ return (v1.intValue() < v2.intValue()) ? v1 : v2;
+ }
+
+ /**
+ * Returns {@code true} if the specified level is loggable by at least one logger.
+ */
+ @Override
+ public boolean isLoggable(final Level level) {
+ return first.isLoggable(level) || second.isLoggable(level);
+ }
+
+ /**
+ * Logs a record at the specified level.
+ */
+ @Override
+ public void log(final Level level, final String message) {
+ first .log(level, message);
+ second.log(level, message);
+ }
+
+ /**
+ * Logs a record at the specified level.
+ */
+ @Override
+ public void log(final Level level, final String message, final Throwable thrown) {
+ first .log(level, message, thrown);
+ second.log(level, message, thrown);
+ }
+
+ @Override public void severe (String message) {first.severe (message); second.severe (message);}
+ @Override public void warning(String message) {first.warning(message); second.warning(message);}
+ @Override public void info (String message) {first.info (message); second.info (message);}
+ @Override public void config (String message) {first.config (message); second.config (message);}
+ @Override public void fine (String message) {first.fine (message); second.fine (message);}
+ @Override public void finer (String message) {first.finer (message); second.finer (message);}
+ @Override public void finest (String message) {first.finest (message); second.finest (message);}
+}
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLogger.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java?rev=1387950&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java Thu Sep 20 10:35:57 2012
@@ -0,0 +1,77 @@
+/*
+ * 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.logging.Logger;
+
+
+/**
+ * Redirects logging to two loggers. This is used only when more than one {@link LoggerFactory}
+ * is found on the classpath. This should never happen, but if it happen anyway we will send the
+ * log records to all registered loggers in order to have a behavior slightly more determinist
+ * than picking an arbitrary logger.
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ * @since 0.3 (derived from geotk-3.20)
+ * @version 0.3
+ * @module
+ */
+final class DualLoggerFactory extends LoggerFactory {
+ /**
+ * The factories of loggers on which to delegate logging events.
+ */
+ private final LoggerFactory> first, second;
+
+ /**
+ * Creates a new factory which will delegate the logging events to the loggers
+ * created by the two given factories.
+ */
+ DualLoggerFactory(final LoggerFactory> first, final LoggerFactory> second) {
+ super(DualLogger.class);
+ this.first = first;
+ this.second = second;
+ }
+
+ /**
+ * Returns the implementation to use for the logger of the specified name,
+ * or {@code null} if the logger would delegates to Java logging anyway.
+ */
+ @Override
+ protected DualLogger getImplementation(final String name) {
+ return new DualLogger(name, first.getLogger(name), second.getLogger(name));
+ }
+
+ /**
+ * Wraps the specified {@linkplain #getImplementation implementation} in a Java logger.
+ */
+ @Override
+ protected Logger wrap(final String name, final DualLogger implementation) {
+ return implementation;
+ }
+
+ /**
+ * Returns the {@linkplain #getImplementation implementation} wrapped by the specified logger,
+ * or {@code null} if none.
+ */
+ @Override
+ protected DualLogger unwrap(final Logger logger) {
+ if (logger instanceof DualLogger) {
+ return (DualLogger) logger;
+ }
+ return null;
+ }
+}
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/DualLoggerFactory.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java?rev=1387950&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java Thu Sep 20 10:35:57 2012
@@ -0,0 +1,726 @@
+/*
+ * 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.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.logging.Filter;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.regex.Pattern;
+
+import org.apache.sis.util.Debug;
+
+
+/**
+ * An adapter that redirect all JDK logging events to an other logging framework. This
+ * class redefines the {@link #severe(String) severe}, {@link #warning(String) warning},
+ * {@link #info(String) info}, {@link #config(String) config}, {@link #fine(String) fine},
+ * {@link #finer(String) finer} and {@link #finest(String) finest} methods as abstract
+ * ones. Subclasses should implement those methods in order to map JDK logging levels to
+ * the backend logging framework.
+ *
+ * All {@link #log(Level,String) log} methods are overridden in order to redirect to one of the
+ * above-cited methods. Note that this is the opposite approach than the JDK logging framework
+ * one, which implements everything on top of {@link Logger#log(LogRecord)}. This adapter is
+ * defined in terms of {@link #severe(String) severe} ⦠{@link #finest(String) finest} methods
+ * instead because external frameworks like Commons-logging
+ * don't work with {@link LogRecord}, and sometime provides nothing else than convenience methods
+ * equivalent to {@link #severe(String) severe} … {@link #finest(String) finest}.
+ *
+ * {@section Restrictions}
+ * Because the configuration is expected to be fully controlled by the external logging
+ * framework, every configuration methods inherited from {@link Logger} are disabled:
+ *
+ *
+ *
{@link #addHandler(Handler)}
+ * since the handling is performed by the external framework.
+ *
+ *
{@link #setUseParentHandlers(boolean)}
+ * since this adapter never delegates to the parent handlers. This is consistent with the
+ * previous item and avoid mixing loggings from the external framework with JDK loggings.
+ *
+ *
{@link #setParent(Logger)}
+ * since this adapter should not inherits any configuration from a parent logger using the
+ * JDK logging framework.
+ *
+ *
{@link #setFilter(Filter)}
+ * for keeping this {@code LoggerAdapter} simple.
+ *
+ *
+ * Since {@code LoggerAdapter}s do not hold any configuration by themselves, it is not strictly
+ * necessary to {@linkplain java.util.logging.LogManager#addLogger add them to the log manager}.
+ * The adapters can be created, garbage-collected and recreated again while preserving their
+ * behavior since their configuration is entirely contained in the external logging framework.
+ *
+ * {@section Localization}
+ * This logger is always created without resource bundles. Localizations must be performed through
+ * explicit calls to {@code logrb} or {@link #log(LogRecord)} methods. This is sufficient for
+ * SIS needs, which performs all localizations through the later. Note that those methods
+ * will be slower in this {@code LoggerAdapter} than the default {@link Logger} because this
+ * adapter localizes and formats records immediately instead of letting the {@linkplain Handler}
+ * performs this work only if needed.
+ *
+ * {@section Logging levels}
+ * If a log record {@linkplain Level level} is not one of the predefined ones, then this class
+ * maps to the first level below the specified one. For example if a log record has some level
+ * between {@link Level#FINE FINE} and {@link Level#FINER FINER}, then the {@link #finer finer}
+ * method will be invoked. See {@link #isLoggable(Level)} for implementation tips taking advantage
+ * of this rule.
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ * @since 0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ *
+ * @see Logging
+ */
+public abstract class LoggerAdapter extends Logger {
+ /**
+ * The pattern to use for detecting {@link MessageFormat}.
+ */
+ private static final Pattern MESSAGE_FORMAT = Pattern.compile("\\{\\d+\\}");
+
+ /**
+ * Creates a new logger.
+ *
+ * @param name The logger name.
+ */
+ protected LoggerAdapter(final String name) {
+ super(name, null);
+ /*
+ * Must invokes the super-class method, because LoggerAdapter overrides it as a no-op.
+ */
+ super.setUseParentHandlers(false);
+ /*
+ * Sets the level to ALL as a matter of principle, but we will never check the level
+ * anyway (we will let the external logging framework do its own check). Note that we
+ * must invoke the method in the super-class because we want to set the java logging
+ * level, not the external framework level.
+ */
+ super.setLevel(Level.ALL);
+ }
+
+ /**
+ * Sets the level for this logger. Subclasses must redirect the call to the external
+ * logging framework, or do nothing if the level can not be changed programmatically.
+ *
+ * @param level The new value for the log level (may be null).
+ */
+ @Override
+ public abstract void setLevel(Level level);
+
+ /**
+ * Returns the level for this logger. Subclasses shall get this level from the
+ * external logging framework.
+ *
+ * @return The logger's level.
+ */
+ @Override
+ public abstract Level getLevel();
+
+ /**
+ * Returns the level for {@link #entering}, {@link #exiting} and {@link #throwing} methods.
+ * The default implementation returns {@link Level#FINER}, which is consistent with the
+ * value used in the JDK logging framework. Subclasses should override this method if
+ * a different debug level is wanted.
+ *
+ * @return The level to use for debugging informations.
+ */
+ @Debug
+ protected Level getDebugLevel() {
+ return Level.FINER;
+ }
+
+ /**
+ * Returns {@code true} if the specified level is loggable.
+ *
+ * {@section Implementation tip}
+ * Given that {@link Level#intValue} for all predefined levels are documented in the {@link Level}
+ * specification and are multiple of 100, given that integer divisions are rounded toward zero and
+ * given rule documented in this class javadoc, then logging levels
+ * can be efficiently mapped to predefined levels using {@code switch} statements as below. This
+ * statement has good chances to be compiled to the {@code tableswitch} bytecode rather than
+ * {@code lookupswitch} (see
+ * Compiling
+ * Switches in The Java Virtual Machine Specification).
+ *
+ * {@preformat java
+ * public boolean isLoggable(Level level) {
+ * final int n = level.intValue();
+ * switch (n / 100) {
+ * default: {
+ * // MAX_VALUE is a special value for Level.OFF. Otherwise and
+ * // if positive, fallthrough since we are greater than SEVERE.
+ * switch (n) {
+ * case Integer.MIN_VALUE: return true; // Level.ALL
+ * case Integer.MAX_VALUE: return false; // Level.OFF
+ * default: if (n < 0) return false;
+ * }
+ * }
+ * case 10: return isSevereEnabled();
+ * case 9: return isWarningEnabled();
+ * case 8: return isInfoEnabled();
+ * case 7: return isConfigEnabled();
+ * case 6: // fallthrough
+ * case 5: return isFineEnabled();
+ * case 4: return isFinerEnabled();
+ * case 3: return isFinestEnabled();
+ * case 2: // fallthrough
+ * case 1: // fallthrough
+ * case 0: return false;
+ * }
+ * }
+ * }
+ *
+ * @param level A message logging level.
+ * @return {@code true} if the given message level is currently being logged.
+ */
+ @Override
+ public abstract boolean isLoggable(Level level);
+
+ /**
+ * Logs a {@link Level#SEVERE SEVERE} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void severe(String message);
+
+ /**
+ * Logs a {@link Level#WARNING WARNING} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void warning(String message);
+
+ /**
+ * Logs an {@link Level#INFO INFO} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void info(String message);
+
+ /**
+ * Logs an {@link Level#CONFIG CONFIG} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void config(String message);
+
+ /**
+ * Logs a {@link Level#FINE FINE} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void fine(String message);
+
+ /**
+ * Logs a {@link Level#FINER FINER} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void finer(String message);
+
+ /**
+ * Logs a {@link Level#FINEST FINEST} message.
+ *
+ * @param message The message to log.
+ */
+ @Override
+ public abstract void finest(String message);
+
+ /**
+ * Logs a method entry to the {@linkplain #getDebugLevel debug level}. Compared to the
+ * default {@link Logger}, this implementation bypass the level check in order to let
+ * the backing logging framework do its own check.
+ *
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of method that is being entered.
+ */
+ @Override
+ public void entering(final String sourceClass, final String sourceMethod) {
+ logp(getDebugLevel(), sourceClass, sourceMethod, "ENTRY");
+ }
+
+ /**
+ * Logs a method entry to the {@linkplain #getDebugLevel debug level} with one parameter.
+ * Compared to the default {@link Logger}, this implementation bypass the level check in
+ * order to let the backing logging framework do its own check.
+ *
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of method that is being entered.
+ * @param param Parameter to the method being entered.
+ */
+ @Override
+ public void entering(String sourceClass, String sourceMethod, Object param) {
+ logp(getDebugLevel(), sourceClass, sourceMethod, "ENTRY {0}", param);
+ }
+
+ /**
+ * Logs a method entry to the {@linkplain #getDebugLevel debug level} with many parameters.
+ * Compared to the default {@link Logger}, this implementation bypass the level check in
+ * order to let the backing logging framework do its own check.
+ *
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of method that is being entered.
+ * @param params Array of parameters to the method being entered.
+ */
+ @Override
+ public void entering(final String sourceClass, final String sourceMethod, final Object[] params) {
+ final String message;
+ if (params == null) {
+ message = "ENTRY";
+ } else switch (params.length) {
+ case 0: message = "ENTRY"; break;
+ case 1: message = "ENTRY {0}"; break;
+ case 2: message = "ENTRY {0} {1}"; break;
+ default: {
+ final StringBuilder builder = new StringBuilder("ENTRY");
+ for (int i=0; i{@linkplain #log(Level,String) log}(level, message).
+ *
+ * @param level One of the message level identifiers.
+ * @param message The message to log.
+ * @param thrown Throwable associated with log message.
+ */
+ @Override
+ public void log(final Level level, final String message, final Throwable thrown) {
+ log(level, message);
+ }
+
+ /**
+ * Logs a record at the specified level. The default implementation delegates to
+ * {@linkplain #log(Level,String,Object[]) log}(level, message, params)
+ * where the {@code params} array is built from the {@code param} object.
+ *
+ * @param level One of the message level identifiers.
+ * @param message The message to log.
+ * @param param Parameter to the method being entered.
+ */
+ @Override
+ public void log(final Level level, final String message, final Object param) {
+ log(level, message, asArray(param));
+ }
+
+ /**
+ * Logs a record at the specified level.
+ * The defaut implementation formats the message immediately, then delegates to
+ * {@linkplain #log(Level,String) log}(level, message).
+ *
+ * @param level One of the message level identifiers.
+ * @param message The message to log.
+ * @param params Array of parameters to the method being entered.
+ */
+ @Override
+ public void log(final Level level, final String message, final Object[] params) {
+ log(level, format(message, params));
+ }
+
+ /**
+ * Logs a record at the specified level. The defaut implementation discards
+ * the source class and source method, then delegates to
+ * {@linkplain #log(Level,String) log}(level, message).
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param message The message to log.
+ */
+ @Override
+ public void logp(final Level level, final String sourceClass, final String sourceMethod,
+ final String message)
+ {
+ log(level, message);
+ }
+
+ /**
+ * Logs a record at the specified level. The defaut implementation discards
+ * the source class and source method, then delegates to
+ * {@linkplain #log(Level,String,Throwable) log}(level, message, thrown).
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param message The message to log.
+ * @param thrown Throwable associated with log message.
+ */
+ @Override
+ public void logp(final Level level, final String sourceClass, final String sourceMethod,
+ final String message, final Throwable thrown)
+ {
+ log(level, message, thrown);
+ }
+
+ /**
+ * Logs a record at the specified level. The defaut implementation delegates to
+ * {@linkplain #logp(Level,String,String,String,Object[]) logp}(level, sourceClass,
+ * sourceMethod, message, params) where the {@code params} array is built from the
+ * {@code param} object.
+ *
+ * Note that {@code sourceClass} and {@code sourceMethod} will be discarded unless the
+ * target {@link #logp(Level,String,String,String) logp} method has been overridden.
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param message The message to log.
+ * @param param Parameter to the method being entered.
+ */
+ @Override
+ public void logp(final Level level, final String sourceClass, final String sourceMethod,
+ final String message, final Object param)
+ {
+ logp(level, sourceClass, sourceMethod, message, asArray(param));
+ }
+
+ /**
+ * Logs a record at the specified level. The defaut implementation formats the message
+ * immediately, then delegates to {@linkplain #logp(Level,String,String,String)
+ * logp}(level, sourceClass, sourceMethod, message).
+ *
+ * Note that {@code sourceClass} and {@code sourceMethod} will be discarded unless the
+ * target {@link #logp(Level,String,String,String) logp} method has been overridden.
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param message The message to log.
+ * @param params Array of parameters to the method being entered.
+ */
+ @Override
+ public void logp(final Level level, final String sourceClass, final String sourceMethod,
+ final String message, final Object[] params)
+ {
+ logp(level, sourceClass, sourceMethod, format(message, params));
+ }
+
+ /**
+ * Logs a localizable record at the specified level. The defaut implementation localizes the
+ * message immediately, then delegates to {@linkplain #logp(Level,String,String,String)
+ * logp}(level, sourceClass, sourceMethod, message).
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param bundleName Name of resource bundle to localize message, or {@code null}.
+ * @param message The message to log.
+ */
+ @Override
+ public void logrb(final Level level, final String sourceClass, final String sourceMethod,
+ final String bundleName, final String message)
+ {
+ logp(level, sourceClass, sourceMethod, localize(bundleName, message));
+ }
+
+ /**
+ * Logs a localizable record at the specified level. The defaut implementation localizes the
+ * message immediately, then delegates to {@linkplain #logp(Level,String,String,String,
+ * Throwable) logp}(level, sourceClass, sourceMethod, message, thrown).
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param bundleName Name of resource bundle to localize message, or {@code null}.
+ * @param message The message to log.
+ * @param thrown Throwable associated with log message.
+ */
+ @Override
+ public void logrb(final Level level, final String sourceClass, final String sourceMethod,
+ final String bundleName, final String message, final Throwable thrown)
+ {
+ logp(level, sourceClass, sourceMethod, localize(bundleName, message), thrown);
+ }
+
+ /**
+ * Logs a localizable record at the specified level. The defaut implementation localizes the
+ * message immediately, then delegates to {@linkplain #logp(Level,String,String,String,
+ * Object) logp}(level, sourceClass, sourceMethod, message, param).
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param bundleName Name of resource bundle to localize message, or {@code null}.
+ * @param message The message to log.
+ * @param param Parameter to the method being entered.
+ */
+ @Override
+ public void logrb(final Level level, final String sourceClass, final String sourceMethod,
+ final String bundleName, final String message, final Object param)
+ {
+ logp(level, sourceClass, sourceMethod, localize(bundleName, message), param);
+ }
+
+ /**
+ * Logs a localizable record at the specified level. The defaut implementation localizes the
+ * message immediately, then delegates to {@linkplain #logp(Level,String,String,String,
+ * Object[]) logp}(level, sourceClass, sourceMethod, message, params).
+ *
+ * @param level One of the message level identifiers.
+ * @param sourceClass Name of class that issued the logging request.
+ * @param sourceMethod Name of the method.
+ * @param bundleName Name of resource bundle to localize message, or {@code null}.
+ * @param message The message to log.
+ * @param params Array of parameters to the method being entered.
+ */
+ @Override
+ public void logrb(final Level level, final String sourceClass, final String sourceMethod,
+ final String bundleName, String message, final Object[] params)
+ {
+ logp(level, sourceClass, sourceMethod, localize(bundleName, message), params);
+ }
+
+ /**
+ * Do nothing since this logger adapter does not supports handlers.
+ * The configuration should be fully controlled by the external logging framework
+ * (e.g. Commons-logging) instead,
+ * which is not expected to use {@link Handler} objects.
+ *
+ * @param handler A logging handler, ignored in default implementation.
+ */
+ @Override
+ public void addHandler(Handler handler) {
+ }
+
+ /**
+ * Do nothing since this logger adapter does not support handlers.
+ *
+ * @param handler A logging handler, ignored in default implementation.
+ */
+ @Override
+ public void removeHandler(Handler handler) {
+ }
+
+ /**
+ * Do nothing since this logger never use parent handlers. This is consistent
+ * with {@link #addHandler} not allowing to add any handlers, and avoid mixing
+ * loggings from the external framework with JDK loggings.
+ *
+ * @param useParentHandlers Ignored in default implementation.
+ */
+ @Override
+ public void setUseParentHandlers(boolean useParentHandlers) {
+ }
+
+ /**
+ * Do nothing since this logger adapter does not support arbitrary parents.
+ * More specifically, it should not inherits any configuration from a parent
+ * logger using the JDK logging framework.
+ *
+ * @param parent Ignored in default implementation.
+ */
+ @Override
+ public void setParent(Logger parent) {
+ }
+
+ /**
+ * Do nothing since this logger adapter does not support filters. It is difficult to query
+ * efficiently the filter in this {@code LoggerAdapter} architecture (e.g. we would need to
+ * make sure that {@link Filter#isLoggable} is invoked only once even if a {@code log} call
+ * is cascaded into many other {@code log} calls, and this test must works in multi-threads
+ * environment).
+ *
+ * @param filter Ignored in default implementation.
+ */
+ @Override
+ public void setFilter(Filter filter) {
+ }
+
+ /**
+ * Wraps the specified object in an array. This is a helper method for
+ * {@code log(..., Object)} methods that delegate their work to {@code log(..., Object[])}
+ */
+ private static Object[] asArray(final Object param) {
+ return (param != null) ? new Object[] {param} : null;
+ }
+
+ /**
+ * Formats the specified message. This is a helper method for
+ * {@code log(..., Object[])} methods that delegate their work to {@code log(...)}
+ */
+ private static String format(String message, final Object[] params) {
+ if (params != null && params.length != 0) {
+ if (MESSAGE_FORMAT.matcher(message).find()) try {
+ message = MessageFormat.format(message, params);
+ } catch (IllegalArgumentException e) {
+ // The default Formatter.messageFormat implementation ignores this exception
+ // and uses the pattern as the message, so we mimic its behavior here.
+ }
+ }
+ return message;
+ }
+
+ /**
+ * Localizes the specified message. This is a helper method for
+ * {@code logrb(...)} methods that delegate their work to {@code logp(...)}
+ */
+ private static String localize(final String bundleName, String message) {
+ if (bundleName != null) try {
+ message = ResourceBundle.getBundle(bundleName).getString(message);
+ } catch (MissingResourceException e) {
+ // The default Formatter.messageFormat implementation ignores this exception
+ // and uses the bundle key as the message, so we mimic its behavior here.
+ }
+ return message;
+ }
+}
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerAdapter.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java?rev=1387950&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java Thu Sep 20 10:35:57 2012
@@ -0,0 +1,114 @@
+/*
+ * 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.logging.Logger;
+import net.jcip.annotations.ThreadSafe;
+
+
+/**
+ * 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.
+ *
+ * @param The type of loggers used for the implementation backend.
+ * This is the type used by external frameworks like Log4J.
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ * @since 0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ *
+ * @see Logging
+ * @see LoggerAdapter
+ */
+@ThreadSafe
+public abstract class LoggerFactory {
+ /**
+ * The logger class. We ask for this information right at construction time in order to
+ * force a {@link NoClassDefFoundError} early rather than only the first time a message
+ * is logged.
+ */
+ private final Class loggerClass;
+
+ /**
+ * Creates a new factory.
+ *
+ * @param loggerClass The class of the wrapped logger.
+ */
+ protected LoggerFactory(final Class loggerClass) {
+ this.loggerClass = loggerClass;
+ }
+
+ /**
+ * Returns the logger of the specified name, or {@code null} if the JDK logging framework
+ * should be used.
+ *
+ * @param name The name of the logger.
+ * @return The logger, or {@code null} if the JDK logging framework should be used.
+ */
+ public Logger getLogger(final String name) {
+ final L target = getImplementation(name);
+ if (target == null) {
+ return null;
+ }
+ return wrap(name, target);
+ }
+
+ /**
+ * Returns the base class of objects to be returned by {@link #getImplementation(String)}.
+ * The class depends on the underlying logging framework (Log4J, SLF4J, etc.).
+ *
+ * @return The type of loggers used for the implementation backend.
+ */
+ public Class getImplementationClass() {
+ return loggerClass;
+ }
+
+ /**
+ * Returns the implementation to use for the logger of the specified name. The object to be
+ * returned depends on the logging framework (Log4J, SLF4J, etc.). If the target
+ * framework redirects logging events to JDK logging, then this method shall return
+ * {@code null} since we should not use wrapper at all.
+ *
+ * @param name The name of the logger.
+ * @return The logger as an object of the target logging framework (Log4J, SLF4J,
+ * etc.), or {@code null} if the target framework would redirect
+ * to the JDK logging framework.
+ */
+ protected abstract L getImplementation(String name);
+
+ /**
+ * Wraps the specified {@linkplain #getImplementation(String) implementation} in a JDK logger.
+ *
+ * @param name The name of the logger.
+ * @param implementation An implementation returned by {@link #getImplementation(String)}.
+ * @return A new logger wrapping the specified implementation.
+ */
+ protected abstract Logger wrap(String name, L implementation);
+
+ /**
+ * Returns the {@linkplain #getImplementation(String) implementation} wrapped by the specified
+ * logger, or {@code null} if none. If the specified logger is not an instance of the expected
+ * class, then this method should returns {@code null}.
+ *
+ * @param logger The logger to test.
+ * @return The implementation wrapped by the specified logger, or {@code null} if none.
+ */
+ protected abstract L unwrap(Logger logger);
+}
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/LoggerFactory.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
URL: http://svn.apache.org/viewvc/incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java?rev=1387950&view=auto
==============================================================================
--- incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java (added)
+++ incubator/sis/trunk/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java Thu Sep 20 10:35:57 2012
@@ -0,0 +1,637 @@
+/*
+ * 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.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;
+import org.apache.sis.util.Static;
+import org.apache.sis.util.Exceptions;
+import org.apache.sis.util.Classes;
+
+import static java.util.Arrays.binarySearch;
+import static org.apache.sis.util.Arrays.insert;
+
+
+/**
+ * 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 Commons-logging or
+ * Log4J.
+ *
+ * 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.
+ *
+ * {@section Configuration}
+ * The log records can be redirected explicitly to an other logging framework using the
+ * following method call (replace {@link LoggerFactory#COMMONS_LOGGING} by
+ * {@link LoggerFactory#LOG4J} or an other framework if desired):
+ *
+ * {@preformat java
+ * Logging.SIS.setLoggerFactory(LoggerFactory.COMMONS_LOGGING);
+ * }
+ *
+ * Note however that the above method invocation 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.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ */
+public final class Logging extends Static {
+ /**
+ * Compares {@link Logging} or {@link String} objects for alphabetical order.
+ */
+ private static final Comparator