You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2024/01/27 16:51:27 UTC
(sis) branch geoapi-4.0 updated: Reduce the verbosity of log record or error message during XML unmarshalling. - Some log records were repeated many times. - JAXBException with very long messages had the message repeated in their causes.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new fce18cb382 Reduce the verbosity of log record or error message during XML unmarshalling. - Some log records were repeated many times. - JAXBException with very long messages had the message repeated in their causes.
fce18cb382 is described below
commit fce18cb3826104e0094a1daf79463baabf442ccd
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Jan 27 17:49:58 2024 +0100
Reduce the verbosity of log record or error message during XML unmarshalling.
- Some log records were repeated many times.
- JAXBException with very long messages had the message repeated in their causes.
---
.../main/org/apache/sis/xml/ReferenceResolver.java | 74 ++++++----
.../main/org/apache/sis/xml/bind/Context.java | 47 +++++--
.../apache/sis/xml/util/ExceptionSimplifier.java | 150 +++++++++++++++++++++
.../apache/sis/xml/util/ExternalLinkHandler.java | 8 +-
.../main/org/apache/sis/io/wkt/Element.java | 3 +-
.../apache/sis/referencing/internal/Resources.java | 5 +
.../sis/referencing/internal/Resources.properties | 1 +
.../referencing/internal/Resources_fr.properties | 1 +
.../sis/referencing/util/ReferencingUtilities.java | 5 +-
.../org/apache/sis/storage/base/PRJDataStore.java | 8 +-
.../org/apache/sis/storage/base/URIDataStore.java | 19 ++-
.../main/org/apache/sis/util/Exceptions.java | 13 +-
.../main/org/apache/sis/util/resources/Errors.java | 15 ++-
.../apache/sis/util/resources/Errors.properties | 3 +-
.../apache/sis/util/resources/Errors_fr.properties | 3 +-
.../resources/ResourceInternationalString.java | 4 +-
16 files changed, 292 insertions(+), 67 deletions(-)
diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/ReferenceResolver.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/ReferenceResolver.java
index 8d5828e801..68ef4e7bfb 100644
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/ReferenceResolver.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/ReferenceResolver.java
@@ -157,6 +157,7 @@ public class ReferenceResolver {
* </ul>
*
* If an object is found but is not of the class declared in {@code type},
+ * or if an {@link Exception} was thrown during object unmarshalling,
* then this method emits a warning and returns {@code null}.
*
* @param <T> the compile-time type of the {@code type} argument.
@@ -173,6 +174,7 @@ public class ReferenceResolver {
return null;
}
final Object object;
+ final short reasonIfNull; // An Errors.Key value with one parameter, or 0.
final Context c = (context instanceof Context) ? (Context) context : Context.current();
if (!href.isAbsolute() && Strings.isNullOrEmpty(href.getPath())) {
/*
@@ -187,10 +189,12 @@ public class ReferenceResolver {
return null;
}
object = Context.getObjectForID(c, fragment);
+ reasonIfNull = Errors.Keys.NotABackwardReference_1;
} else try {
/*
- * URI to an external document. We let `ExternalLinkHandler` decide how to replace relative URI
- * by absolute URI. It may depend on whether user has specified a `javax.xml.stream.XMLResolver`
+ * URI to an external document. If a `javax.xml.stream.XMLResolver` property was set on the unmarshaller,
+ * use the user-supplied `URIResolver`. If there is no URI resolver or the URI resolver can not resolve,
+ * fallback on the Apache SIS `ExternalLinkHandler` implementation. The latter is the usual case.
*/
final ExternalLinkHandler handler = Context.linkHandler(c);
Source source = null;
@@ -200,32 +204,38 @@ public class ReferenceResolver {
source = externalSourceResolver.resolve(href.toString(), base.toString());
}
}
- if (source == null) {
- source = handler.openReader(href);
+ if (source == null && (source = handler.openReader(href)) == null) {
+ reasonIfNull = Errors.Keys.CanNotResolveAsAbsolutePath_1;
+ object = null;
+ } else {
+ object = resolveExternal(context, source);
+ reasonIfNull = 0;
}
- object = (source != null) ? resolveExternal(context, source) : null;
} catch (Exception e) {
ExternalLinkHandler.warningOccured(href, e);
return null;
}
/*
- * At this point, the referenced object has been fetched.
- * Verify its validity.
+ * At this point, the referenced object has been fetched or unmarshalled.
+ * The result may be null, in which case the warning to emit depends on the
+ * reason why the object is null: could not resolve, or could not unmarshall.
*/
if (type.isInstance(object)) {
return type.cast(object);
- } else {
- final short key;
- final Object[] args;
- if (object == null) {
- key = Errors.Keys.NotABackwardReference_1;
- args = new Object[] {href.toString()};
- } else {
- key = Errors.Keys.UnexpectedTypeForReference_3;
- args = new Object[] {href.toString(), type, object.getClass()};
+ }
+ final short key;
+ final Object[] args;
+ if (object == null) {
+ if (reasonIfNull == 0) {
+ return null;
}
- Context.warningOccured(c, ReferenceResolver.class, "resolve", Errors.class, key, args);
+ key = reasonIfNull;
+ args = new Object[] {href.toString()};
+ } else {
+ key = Errors.Keys.UnexpectedTypeForReference_3;
+ args = new Object[] {href.toString(), type, object.getClass()};
}
+ Context.warningOccured(c, ReferenceResolver.class, "resolve", Errors.class, key, args);
return null;
}
@@ -245,9 +255,18 @@ public class ReferenceResolver {
* </ul>
* The resolved URL, if known, should be available in {@link Source#getSystemId()}.
*
+ * <h4>Error handling</h4>
+ * The default implementation keeps a cache during the execution of an {@code XML.unmarshall(…)} method
+ * (or actually, during a {@linkplain MarshallerPool pooled unmarshaller} method).
+ * If an exception is thrown during the document unmarshalling, this failure is also recorded in the cache.
+ * Therefor, the exception is thrown only during the first attempt to read the document
+ * and {@code null} is returned directly on next attempts for the same source.
+ * Exceptions thrown by this method are caught by {@link #resolve(MarshalContext, Class, XLink) resolve(…)}
+ * and reported as warnings.
+ *
* @param context context (GML version, locale, <i>etc.</i>) of the (un)marshalling process.
* @param source source of the document specified by the {@code xlink:href} attribute value.
- * @return an object for the given source, or {@code null} if none.
+ * @return an object for the given source, or {@code null} if none, for example because of failure in a previous attempt.
* @throws Exception if an error occurred while opening or parsing the document.
*
* @since 1.5
@@ -278,12 +297,12 @@ public class ReferenceResolver {
* and the URI fragment to use as a GML identifier. Check if the document is in the cache.
* Note that if the fragment is null, then by convention we lookup for the whole document.
*/
- final Context c = Context.current();
+ final Context c = (context instanceof Context) ? (Context) context : Context.current();
if (c != null) {
final Object object = c.getExternalObjectForID(document, fragment);
if (object != null) {
XmlUtilities.close(source);
- return object;
+ return (object != Context.INVALID_OBJECT) ? object : null;
}
}
/*
@@ -297,10 +316,17 @@ public class ReferenceResolver {
((Pooled) m).forIncludedDocument(document);
}
final Object object;
- if (uri != null) {
- object = m.unmarshal(uri.toURL());
- } else {
- object = m.unmarshal(source);
+ try {
+ if (uri != null) {
+ object = m.unmarshal(uri.toURL());
+ } else {
+ object = m.unmarshal(source);
+ }
+ } catch (Exception e) {
+ if (c != null) {
+ c.cacheDocument(document, Context.INVALID_OBJECT);
+ }
+ throw e;
}
pool.recycle(m);
/*
diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java
index 6558346f42..e66c25496d 100644
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java
@@ -117,8 +117,19 @@ public final class Context extends MarshalContext {
*/
private static final ThreadLocal<Context> CURRENT = new ThreadLocal<>();
+ /**
+ * A sentinel value meaning that unmarshalling of a document was already attempted before and failed.
+ * This is used for documents referenced from a larger document using {@code xlink:href}.
+ *
+ * @see #getExternalObjectForID(Object, String)
+ * @see #cacheDocument(Object, Object)
+ */
+ public static final Object INVALID_OBJECT = Void.TYPE;
+
/**
* The logger to use for warnings that are specific to XML.
+ *
+ * @see #warningOccured(Context, Level, Class, String, Throwable, Class, short, Object...)
*/
public static final Logger LOGGER = Logger.getLogger(Loggers.XML);
@@ -180,6 +191,10 @@ public final class Context extends MarshalContext {
* At marhalling time, this map is used for avoiding duplicated identifiers in the same XML document.
* At unmarshalling time, this is used for getting a previously unmarshalled object from its identifier.
*
+ * <p>By convention, the {@code null} key is associated to the whole document. This convention is used if
+ * the document being unmarshalled is part of a larger document and was referenced by {@code xlink:href}.
+ * In such case, this map is also a value of the {@link #documentToXmlids} map.</p>
+ *
* @see #getObjectForID(Context, String)
*/
private final Map<String,Object> xmlidToObject;
@@ -202,8 +217,10 @@ public final class Context extends MarshalContext {
* from a file or an URL, {@code systemId} should be the value of {@link java.net.URI#toASCIIString()} for
* consistency with {@link javax.xml.transform.stream.StreamSource}. However, URI instances are preferred
* in this map because the {@link URI#equals(Object)} method applies some rules regarding case-sensitivity
- * that {@link String#equals(Object)} cannot know. Values of the map are the {@link #xmlidToObject} maps of
- * the corresponding document. By convention, the object associated to the null key is the whole document.</p>
+ * that {@link String#equals(Object)} cannot know.</p>
+ *
+ * <p>Values of this map are the {@link #xmlidToObject} maps of the corresponding document.
+ * See {@link #xmlidToObject} for a description of the meaning of those maps.</p>
*/
private final Map<Object, Map<String,Object>> documentToXmlids;
@@ -219,6 +236,8 @@ public final class Context extends MarshalContext {
/**
* The object to inform about warnings, or {@code null} if none.
+ *
+ * @see org.apache.sis.xml.XML#WARNING_FILTER
*/
private final Filter logFilter;
@@ -679,12 +698,20 @@ public final class Context extends MarshalContext {
* By convention, a null {@code id} returns the whole document.</p>
*
* @param systemId document identifier (without the fragment part) as an {@link URI} or a {@link String}.
- * @param id the fragment part of the URI identifying the object to get.
- * @return the object associated to the given identifier, or {@code null} if none.
+ * @param fragment the fragment part of the URI, or {@code null} for the whole document.
+ * @return the object associated to the given identifier, or {@code null} if none,
+ * or {@link #INVALID_OBJECT} if a parsing was previously attempted and failed.
*/
- public final Object getExternalObjectForID(final Object systemId, final String id) {
+ public final Object getExternalObjectForID(final Object systemId, final String fragment) {
final Map<String,Object> cache = documentToXmlids.get(systemId);
- return (cache != null) ? cache.get(id) : null;
+ if (cache == null) {
+ return null;
+ }
+ final Object value = cache.get(fragment);
+ if (value == null && cache.get(null) == INVALID_OBJECT) {
+ return INVALID_OBJECT;
+ }
+ return value;
}
/**
@@ -693,7 +720,7 @@ public final class Context extends MarshalContext {
* The fragment part is obtained by {@link #getExternalObjectForID(Object, String)}.
*
* @param systemId document identifier (without the fragment part) as an {@link URI} or a {@link String}.
- * @param document the document to cache.
+ * @param document the document to cache, or {@link #INVALID_OBJECT} for recording a failure to read the document.
*/
public final void cacheDocument(final Object systemId, final Object document) {
final Map<String, Object> cache = documentToXmlids.get(systemId);
@@ -766,7 +793,7 @@ public final class Context extends MarshalContext {
/**
* Sends a warning to the warning listener if there is one, or logs the warning otherwise.
- * In the latter case, this method logs to the given logger.
+ * In the latter case, this method logs to {@link #LOGGER}.
*
* <p>If the given {@code resources} is {@code null}, then this method will build the log
* message from the {@code exception}.</p>
@@ -776,7 +803,7 @@ public final class Context extends MarshalContext {
* @param classe the class to declare as the warning source.
* @param method the name of the method to declare as the warning source.
* @param exception the exception thrown, or {@code null} if none.
- * @param resources either {@code Errors.class}, {@code Messages.class} or {@code null} for the exception message.
+ * @param resources either {@code Errors.class} or {@code Messages.class}, or {@code null} for the exception message.
* @param key the resource keys as one of the constants defined in the {@code Keys} inner class.
* @param arguments the arguments to be given to {@code MessageFormat} for formatting the log message.
*/
@@ -837,7 +864,7 @@ public final class Context extends MarshalContext {
/**
* Convenience method for sending a warning for the given exception.
- * The logger will be {@code "org.apache.sis.xml"}.
+ * The log message will be the message of the given exception.
*
* @param context the current context, or {@code null} if none.
* @param classe the class to declare as the warning source.
diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExceptionSimplifier.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExceptionSimplifier.java
new file mode 100644
index 0000000000..a7cac1b5b1
--- /dev/null
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExceptionSimplifier.java
@@ -0,0 +1,150 @@
+/*
+ * 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.xml.util;
+
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import org.xml.sax.SAXParseException;
+import org.apache.sis.system.Loggers;
+import org.apache.sis.xml.bind.Context;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * Simplifies an exception before to log it. This class reduces log flooding when an exception has a very long message
+ * (e.g. JAXB enumerating all elements that it was expecting) and that long message is repeated in the exception cause.
+ * This class also handles the identification of the location where the error occurred.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+public final class ExceptionSimplifier {
+ /**
+ * The key to use for producing an error message, or 0 for using the exception message.
+ * Shall be a constant from {@link Errors.Keys}.
+ */
+ private short errorKey;
+
+ /**
+ * The values to insert in the error message, or {@code null} for using the exception message.
+ */
+ private Object[] errorValues;
+
+ /**
+ * The exception.
+ */
+ public final Exception exception;
+
+ /**
+ * Simplifies the given exception if possible, and produces an error message.
+ *
+ * @param source the source (URI or Path) that couldn't be parsed, or {@code null} if unknown.
+ * @param exception the error that occurred while parsing the source.
+ */
+ public ExceptionSimplifier(Object source, Exception exception) {
+ int line = -1, column = -1;
+ for (Throwable cause = exception; cause != null; cause = cause.getCause()) {
+ if (cause instanceof SAXParseException) {
+ var s = (SAXParseException) cause;
+ if ((line | column) < 0) {
+ line = s.getLineNumber();
+ column = s.getColumnNumber();
+ }
+ if (source == null) {
+ source = s.getPublicId();
+ if (source == null) {
+ source = s.getSystemId();
+ }
+ }
+ }
+ if (cause != exception) {
+ final String msg = exception.getMessage();
+ if (msg != null) {
+ final String s = cause.getMessage();
+ if (s != null && !msg.contains(s)) break;
+ }
+ if (exception.getClass().isInstance(cause)) {
+ exception = (Exception) cause;
+ }
+ }
+ }
+ this.exception = exception;
+ if (source == null) {
+ final ExternalLinkHandler handler = Context.linkHandler(Context.current());
+ if (handler != null) {
+ source = handler.getBase();
+ }
+ }
+ if (source != null) {
+ if ((line | column) < 0) {
+ errorKey = Errors.Keys.CanNotRead_1;
+ errorValues = new Object[] {source};
+ } else {
+ errorKey = Errors.Keys.CanNotRead_3;
+ errorValues = new Object[] {source, line, column};
+ }
+ }
+ }
+
+ /**
+ * Returns the error message.
+ *
+ * @param locale desired locale for the error message.
+ * @return the error message, or {@code null} if none.
+ */
+ public String getMessage(final Locale locale) {
+ if (errorKey != 0) {
+ return Errors.getResources(locale).getString(errorKey, errorValues);
+ } else {
+ return exception.getMessage();
+ }
+ }
+
+ /**
+ * Sends the exception to the warning listener if there is one, or logs the warning otherwise.
+ * In the latter case, this method logs to {@link Context#LOGGER}.
+ *
+ * @param context the current context, or {@code null} if none.
+ * @param classe the class to declare as the warning source.
+ * @param method the name of the method to declare as the warning source.
+ */
+ public void report(final Context context, final Class<?> classe, final String method) {
+ Context.warningOccured(context, Level.WARNING, classe, method, exception,
+ (errorKey != 0) ? Errors.class : null, errorKey, errorValues);
+ }
+
+ /**
+ * Creates a log record for the warning.
+ *
+ * @param classe the class to declare as the warning source.
+ * @param method the name of the method to declare as the warning source.
+ * @return the log record.
+ */
+ public LogRecord record(final Class<?> classe, final String method) {
+ final LogRecord record;
+ if (errorKey != 0) {
+ record = Errors.getResources((Locale) null).getLogRecord(Level.WARNING, errorKey, errorValues);
+ } else {
+ record = new LogRecord(Level.WARNING, exception.getMessage());
+ }
+ record.setLoggerName(Loggers.XML);
+ record.setSourceClassName(classe.getCanonicalName());
+ record.setSourceMethodName(method);
+ record.setThrown(exception);
+ return record;
+ }
+}
diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java
index 9dbbdc9a22..06fdeddf4d 100644
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java
@@ -21,7 +21,6 @@ import java.io.InputStream;
import java.net.URL;
import java.net.URI;
import java.net.URISyntaxException;
-import java.util.logging.Level;
import javax.xml.stream.Location;
import javax.xml.stream.XMLResolver;
import javax.xml.stream.XMLInputFactory;
@@ -181,12 +180,11 @@ public class ExternalLinkHandler {
* The latter assumption is valid if {@code ReferenceResolver.resolve(…)} is the only
* code invoking, directly or indirectly, this {@code warning(…)} method.
*
- * @param href the URI that cannot be parsed.
- * @param cause the exception that occurred while trying to process the document.
+ * @param href the URI that cannot be parsed.
+ * @param cause the exception that occurred while trying to process the document.
*/
public static void warningOccured(final Object href, final Exception cause) {
- Context.warningOccured(Context.current(), Level.WARNING, ReferenceResolver.class, "resolve",
- cause, Errors.class, Errors.Keys.CanNotRead_1, href);
+ new ExceptionSimplifier(href, cause).report(Context.current(), ReferenceResolver.class, "resolve");
}
/**
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Element.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Element.java
index 8b0f5bd021..425f7808bb 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Element.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Element.java
@@ -31,6 +31,7 @@ import org.apache.sis.util.Exceptions;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.referencing.util.WKTKeywords;
+import org.apache.sis.referencing.internal.Resources;
import org.apache.sis.util.internal.CollectionsExt;
import static org.apache.sis.util.CharSequences.skipLeadingWhitespaces;
@@ -404,7 +405,7 @@ final class Element {
* @return the exception to be thrown.
*/
final ParseException parseFailed(final Exception cause) {
- return new UnparsableObjectException(errorLocale, Errors.Keys.ErrorIn_2,
+ return new UnparsableObjectException(errorLocale, Resources.Keys.CannotParseElement_2,
new String[] {keyword, Exceptions.getLocalizedMessage(cause, errorLocale)}, offset).initCause(cause);
}
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java
index df0be0c83f..ac36d0818d 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java
@@ -167,6 +167,11 @@ public class Resources extends IndexedResourceBundle {
*/
public static final short CanNotUseGeodeticParameters_2 = 9;
+ /**
+ * Cannot parse the “{0}” element: {1}
+ */
+ public static final short CannotParseElement_2 = 101;
+
/**
* Axis directions {0} and {1} are colinear.
*/
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.properties b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.properties
index 18b3aba29d..1ceec59dae 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.properties
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.properties
@@ -22,6 +22,7 @@
# Information messages or non-fatal warnings
#
AmbiguousEllipsoid_1 = Ambiguity between inverse flattening and semi minor axis length for \u201c{0}\u201d. Using inverse flattening.
+CannotParseElement_2 = Cannot parse the \u201c{0}\u201d element: {1}
ConformanceMeansDatumShift = This result indicates if a datum shift method has been applied.
ConstantProjParameterValue_1 = This parameter is shown for completeness, but should never have a value different than {0} for this projection.
DeprecatedCode_3 = Code \u201c{0}\u201d is deprecated and replaced by code {1}. Reason is: {2}
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources_fr.properties b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources_fr.properties
index 98e953dc70..27a9408e59 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources_fr.properties
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources_fr.properties
@@ -27,6 +27,7 @@
# Information messages or non-fatal warnings
#
AmbiguousEllipsoid_1 = Ambigu\u00eft\u00e9 entre l\u2019aplatissement et la longueur du semi-axe mineur pour \u00ab\u202f{0}\u202f\u00bb. Utilise l\u2019aplatissement.
+CannotParseElement_2 = Ne peut pas d\u00e9coder l\u2019\u00e9l\u00e9ment \u00ab\u202f{0}\u202f\u00bb\u00a0: {1}
ConformanceMeansDatumShift = Ce r\u00e9sultat indique si un changement de r\u00e9f\u00e9rentiel a \u00e9t\u00e9 appliqu\u00e9.
ConstantProjParameterValue_1 = Ce param\u00e8tre est montr\u00e9 pour \u00eatre plus complet, mais sa valeur ne devrait jamais \u00eatre diff\u00e9rente de {0} pour cette projection.
DeprecatedCode_3 = Le code \u00ab\u202f{0}\u202f\u00bb est d\u00e9pr\u00e9ci\u00e9 et remplac\u00e9 par le code {1}. La raison est\u00a0: {2}
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/ReferencingUtilities.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/ReferencingUtilities.java
index d0bb28fc88..4234653bd1 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/ReferencingUtilities.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/ReferencingUtilities.java
@@ -19,6 +19,7 @@ package org.apache.sis.referencing.util;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
+import java.util.NoSuchElementException;
import javax.measure.Unit;
import javax.measure.quantity.Angle;
import org.opengis.annotation.UML;
@@ -220,6 +221,7 @@ public final class ReferencingUtilities extends Static {
* @param addTo where to add the single CRS in order to obtain a flat view of {@code source}.
* @return {@code true} if this method found only single CRS in {@code source}, in which case {@code addTo}
* got the same content (assuming that {@code addTo} was empty prior this method call).
+ * @throws NoSuchElementException if a CRS component is missing.
* @throws ClassCastException if a CRS is neither a {@link SingleCRS} or a {@link CompoundCRS}.
*
* @see org.apache.sis.referencing.CRS#getSingleComponents(CoordinateReferenceSystem)
@@ -243,10 +245,11 @@ public final class ReferencingUtilities extends Static {
final String message;
if (candidate instanceof NilObject) {
message = Errors.format(Errors.Keys.NilObject_1, Identifiers.getNilReason((NilObject) candidate));
+ throw new NoSuchElementException(message);
} else {
message = Errors.format(Errors.Keys.NestedElementNotAllowed_1, getInterface(candidate));
+ throw new ClassCastException(message);
}
- throw new ClassCastException(message);
}
}
return sameContent;
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/PRJDataStore.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/PRJDataStore.java
index dbfeba3ab8..7dcbe0f4c9 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/PRJDataStore.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/PRJDataStore.java
@@ -47,6 +47,7 @@ import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.Classes;
import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.xml.util.ExceptionSimplifier;
/**
@@ -138,9 +139,12 @@ public abstract class PRJDataStore extends URIDataStore {
listeners.warning(cannotReadAuxiliaryFile(extension));
return Optional.empty();
}
- if (content.source != null) {
+ if (content.source != null) try {
// ClassCastException handled by `catch` statement below.
return Optional.of(type.cast(readXML(content.source)));
+ } catch (JAXBException e) {
+ var s = new ExceptionSimplifier(content.getFilename(), e);
+ throw new DataStoreException(s.getMessage(getLocale()), s.exception);
}
final String wkt = content.toString();
final StoreFormat format = new StoreFormat(dataLocale, timezone, null, listeners);
@@ -165,7 +169,7 @@ public abstract class PRJDataStore extends URIDataStore {
} catch (NoSuchFileException | FileNotFoundException e) {
listeners.warning(cannotReadAuxiliaryFile(extension), e);
return Optional.empty();
- } catch (IOException | ParseException | JAXBException | ClassCastException e) {
+ } catch (IOException | ParseException | ClassCastException e) {
cause = e;
}
final var e = new DataStoreReferencingException(cannotReadAuxiliaryFile(extension), cause);
diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/URIDataStore.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/URIDataStore.java
index 3427bb2b73..0862772199 100644
--- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/URIDataStore.java
+++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/URIDataStore.java
@@ -51,6 +51,7 @@ import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.iso.Names;
import org.apache.sis.xml.XML;
import org.apache.sis.xml.util.URISource;
+import org.apache.sis.xml.util.ExceptionSimplifier;
/**
@@ -328,31 +329,35 @@ public abstract class URIDataStore extends DataStore implements StoreResource, R
* @param builder where to merge the metadata.
*/
protected final void mergeAuxiliaryMetadata(final MetadataBuilder builder) {
+ Object spec = null; // Used only for formatting error message.
Object metadata = null;
- Exception error = null;
try {
final URI source;
final InputStream input;
final Path path = getMetadataPath();
if (path != null) {
+ spec = path;
source = path.toUri();
input = open(path);
} else {
source = getMetadataURI();
if (source == null) return;
+ spec = source;
input = source.toURL().openStream();
}
metadata = readXML(input, source);
} catch (URISyntaxException | IOException e) {
- error = e;
+ listeners.warning(cannotReadAuxiliaryFile("xml"), e);
} catch (JAXBException e) {
- final Throwable cause = e.getCause();
- error = (cause instanceof IOException) ? (Exception) cause : e;
+ Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ listeners.warning(cannotReadAuxiliaryFile("xml"), (Exception) cause);
+ } else {
+ listeners.warning(new ExceptionSimplifier(spec, e).record(URIDataStore.class, "mergeAuxiliaryMetadata"));
+ }
}
if (metadata != null) {
builder.mergeMetadata(metadata, getLocale());
- } else if (error != null) {
- listeners.warning(cannotReadAuxiliaryFile("xml"), error);
}
}
@@ -384,7 +389,7 @@ public abstract class URIDataStore extends DataStore implements StoreResource, R
java.util.logging.Filter handler = (record) -> {
record.setLoggerName(null); // For allowing `listeners` to use the provider's logger name.
listeners.warning(record);
- return true;
+ return false;
};
// Cannot use Map.of(…) because it does not accept null values.
Map<String,Object> properties = new HashMap<>(8);
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Exceptions.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Exceptions.java
index bfd796116e..f1d6514640 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Exceptions.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Exceptions.java
@@ -192,15 +192,11 @@ public final class Exceptions extends Static {
* <li>It is an instance of {@link BackingStoreException} (typically wrapping a checked exception).</li>
* <li>It is an instance of {@link UncheckedIOException} (wrapping a {@link java.io.IOException}).</li>
* <li>It is an instance of {@link DirectoryIteratorException} (wrapping a {@link java.io.IOException}).</li>
- * <li>It is a parent type of the cause. For example, some JDBC drivers wrap {@link SQLException}
- * in other {@code SQLException} without additional information.</li>
+ * <li>It is a parent type of its cause. For example, some JDBC drivers wrap {@link SQLException} in another
+ * {@code SQLException} without additional information. When the wrapper is a parent class of the cause,
+ * details about the reason are less accessible.</li>
* </ul>
*
- * <div class="note"><b>Note:</b>
- * {@link java.security.PrivilegedActionException} is also a wrapper exception, but is not included in above list
- * because it is used in very specific contexts. Furthermore, classes related to security manager are deprecated
- * since Java 17.</div>
- *
* This method uses only the exception class as criterion;
* it does not verify if the exception messages are the same.
*
@@ -233,6 +229,9 @@ public final class Exceptions extends Static {
/**
* Unwraps and copies suppressed exceptions from the given source to the given target.
+ *
+ * @param source the exception from which to copy suppressed exceptions.
+ * @param target the exception where to add suppressed exceptions.
*/
private static void copySuppressed(final Exception source, final Throwable target) {
for (final Throwable suppressed : source.getSuppressed()) {
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
index dfe30d9602..0aba100be6 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
@@ -153,11 +153,21 @@ public class Errors extends IndexedResourceBundle {
*/
public static final short CanNotRead_1 = 12;
+ /**
+ * Cannot read “{0}” at line {1}, column {2}.
+ */
+ public static final short CanNotRead_3 = 34;
+
/**
* Cannot represent “{1}” in a strictly standard-compliant {0} format.
*/
public static final short CanNotRepresentInFormat_2 = 13;
+ /**
+ * Cannot resolve “{0}” as an absolute path.
+ */
+ public static final short CanNotResolveAsAbsolutePath_1 = 205;
+
/**
* Cannot set a value for parameter “{0}”.
*/
@@ -288,11 +298,6 @@ public class Errors extends IndexedResourceBundle {
*/
public static final short ErrorInFileAtLine_2 = 33;
- /**
- * Error in “{0}”: {1}
- */
- public static final short ErrorIn_2 = 34;
-
/**
* A size of {1} elements is excessive for the “{0}” list.
*/
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
index 46fab02978..f134a437e3 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
@@ -42,7 +42,9 @@ CanNotParse_1 = Cannot parse \u201c{0}\u201d.
CanNotProcessProperty_2 = Cannot process property \u201c{0}\u201d. The reason is: {1}
CanNotProcessPropertyAtPath_3 = Cannot process property \u201c{1}\u201d located at path \u201c{0}\u201d. The reason is: {2}
CanNotRead_1 = Cannot read \u201c{0}\u201d.
+CanNotRead_3 = Cannot read \u201c{0}\u201d at line {1}, column {2}.
CanNotReadPropertyInFile_2 = Cannot read property \u201c{1}\u201d in file \u201c{0}\u201d.
+CanNotResolveAsAbsolutePath_1 = Cannot resolve \u201c{0}\u201d as an absolute path.
CanNotRepresentInFormat_2 = Cannot represent \u201c{1}\u201d in a strictly standard-compliant {0} format.
CanNotSetParameterValue_1 = Cannot set a value for parameter \u201c{0}\u201d.
CanNotSetPropertyValue_1 = Cannot set a value for property \u201c{0}\u201d.
@@ -69,7 +71,6 @@ EmptyArgument_1 = Argument \u2018{0}\u2019 shall not be empty.
EmptyDictionary = The dictionary shall contain at least one entry.
EmptyEnvelope2D = Envelope must be at least two-dimensional and non-empty.
EmptyProperty_1 = Property named \u201c{0}\u201d shall not be empty.
-ErrorIn_2 = Error in \u201c{0}\u201d: {1}
ErrorInFileAtLine_2 = An error occurred in file \u201c{0}\u201d at line {1}.
ExcessiveListSize_2 = A size of {1} elements is excessive for the \u201c{0}\u201d list.
ExcessiveNumberOfDimensions_1 = For this algorithm, {0} is an excessive number of dimensions.
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
index c5cb2922ab..59f896eec0 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
@@ -39,7 +39,9 @@ CanNotParse_1 = Ne peut pas interpr\u00e9ter \u00ab\u202f{0}
CanNotProcessProperty_2 = Ne peut pas traiter la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb pour la raison suivante\u00a0: {1}
CanNotProcessPropertyAtPath_3 = Ne peut pas traiter la propri\u00e9t\u00e9 \u00ab\u202f{1}\u202f\u00bb d\u00e9sign\u00e9e par le chemin \u00ab\u202f{0}\u202f\u00bb pour la raison suivante\u00a0: {2}
CanNotRead_1 = Ne peut pas lire \u00ab\u202f{0}\u202f\u00bb.
+CanNotRead_3 = Ne peut pas lire \u00ab\u202f{0}\u202f\u00bb \u00e0 la ligne {1}, colonne {2}.
CanNotReadPropertyInFile_2 = Ne peut pas lire la propri\u00e9t\u00e9 \u00ab\u202f{1}\u202f\u00bb dans le fichier \u00ab\u202f{0}\u202f\u00bb.
+CanNotResolveAsAbsolutePath_1 = Ne peut pas r\u00e9soudre \u00ab\u202f{0}\u202f\u00bb comme un chemin absolu.
CanNotRepresentInFormat_2 = Ne peut pas repr\u00e9senter \u00ab\u202f{1}\u202f\u00bb dans un format {0} strictement conforme.
CanNotSetParameterValue_1 = Ne peut pas d\u00e9finir une valeur pour le param\u00e8tre \u00ab\u202f{0}\u202f\u00bb.
CanNotSetPropertyValue_1 = Ne peut pas d\u00e9finir une valeur pour la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb.
@@ -66,7 +68,6 @@ EmptyArgument_1 = L\u2019argument \u2018{0}\u2019 ne doit pas
EmptyDictionary = Le dictionnaire doit contenir au moins une entr\u00e9e.
EmptyEnvelope2D = L\u2019enveloppe doit avoir au moins deux dimensions et ne pas \u00eatre vide.
EmptyProperty_1 = La propri\u00e9t\u00e9 nomm\u00e9e \u00ab\u202f{0}\u202f\u00bb ne doit pas \u00eatre vide.
-ErrorIn_2 = Erreur dans \u00ab\u202f{0}\u202f\u00bb\u00a0: {1}
ErrorInFileAtLine_2 = Une erreur est survenue dans le fichier \u00ab\u202f{0}\u202f\u00bb \u00e0 la ligne {1}.
ExcessiveListSize_2 = Une taille de {1} \u00e9l\u00e9ments est excessive pour la liste \u00ab\u202f{0}\u202f\u00bb.
ExcessiveNumberOfDimensions_1 = Pour cet algorithme, {0} est un trop grand nombre de dimensions.
diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/ResourceInternationalString.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/ResourceInternationalString.java
index ca01478095..333e437107 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/ResourceInternationalString.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/ResourceInternationalString.java
@@ -113,10 +113,8 @@ public abstract class ResourceInternationalString extends AbstractInternationalS
* @return a log record with the message of this international string.
*/
public final LogRecord toLogRecord(final Level level) {
- final LogRecord record = new LogRecord(level, getKeyConstants().getKeyName(key));
final IndexedResourceBundle resources = getBundle(null);
- record.setResourceBundleName(resources.getClass().getName());
- record.setResourceBundle(resources);
+ final LogRecord record = resources.getLogRecord(level, key);
if (hasArguments) {
record.setParameters(resources.toArray(arguments));
}