Author: desruisseaux Date: Tue May 22 16:35:40 2018 New Revision: 1832046 URL: http://svn.apache.org/viewvc?rev=1832046&view=rev Log: Reduce the amount of exceptions logged when the application can not connect to the spatial metadata database.
Added: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataFallback.java (with props) Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java sis/branches/JDK8/core/sis-referencing-by-identifiers/pom.xml sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/report/CoordinateReferenceSystems.java Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java?rev=1832046&r1=1832045&r2=1832046&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java [UTF-8] (original) +++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Initializer.java [UTF-8] Tue May 22 16:35:40 2018 @@ -70,7 +70,7 @@ import org.apache.sis.util.logging.Loggi * All other methods are related to getting the {@code DataSource} instance, through JNDI or otherwise. * * @author Martin Desruisseaux (Geomatys) - * @version 0.8 + * @version 1.0 * @since 0.7 * @module */ @@ -422,9 +422,10 @@ public abstract class Initializer { * This message can be used for constructing an exception when {@link #getDataSource()} returned {@code null}. * * @param locale the locale for the message to produce, or {@code null} for the default one. + * @param asLog {@code true} for returning the message as a {@link LogRecord}, {@code false} for a {@link String}. * @return message for unspecified data source. */ - public static String unspecified(final Locale locale) { + public static Object unspecified(final Locale locale, final boolean asLog) { final short key; final String value; if (hasJNDI()) { @@ -434,7 +435,8 @@ public abstract class Initializer { key = Messages.Keys.DataDirectoryNotSpecified_1; value = DataDirectory.ENV; } - return Messages.getResources(locale).getString(key, value); + final Messages resources = Messages.getResources(locale); + return asLog ? resources.getLogRecord(Level.WARNING, key, value) : resources.getString(key, value); } /** @@ -486,7 +488,7 @@ public abstract class Initializer { */ private static DataSource forJavaDB(final String path, final ClassLoader loader) throws Exception { final Class<?> c = Class.forName("org.apache.derby.jdbc.EmbeddedDataSource", true, loader); - final DataSource ds = (DataSource) c.newInstance(); + final DataSource ds = (DataSource) c.getConstructor().newInstance(); final Class<?>[] args = {String.class}; c.getMethod("setDatabaseName", args).invoke(ds, path); c.getMethod("setDataSourceName", args).invoke(ds, "Apache SIS spatial metadata"); Added: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataFallback.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataFallback.java?rev=1832046&view=auto ============================================================================== --- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataFallback.java (added) +++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataFallback.java [UTF-8] Tue May 22 16:35:40 2018 @@ -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.metadata.sql; + +import org.opengis.util.ControlledVocabulary; +import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.logging.WarningListener; +import org.apache.sis.xml.NilReason; + + +/** + * A fallback providing hard-coded values of metadata entities. + * Used when connection to the spatial metadata can not be established. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.0 + * @since 1.0 + * @module + */ +final class MetadataFallback extends MetadataSource { + /** + * The unique instance of this fallback. + */ + static final MetadataFallback INSTANCE = new MetadataFallback(); + + /** + * Creates the singleton. + */ + private MetadataFallback() { + } + + /** + * Searches for the given metadata in the hard-coded list. + * + * @param metadata the metadata to search for. + * @return the identifier of the given metadata, or {@code null} if none. + */ + @Override + public String search(final Object metadata) { + ArgumentChecks.ensureNonNull("metadata", metadata); + return null; + } + + /** + * Returns a hard-coded metadata filled with the data referenced by the specified identifier. + * Alternatively, this method can also return a {@code CodeList} or {@code Enum} element. + * + * @param <T> the parameterized type of the {@code type} argument. + * @param type the interface to implement, or {@code CodeList} or some {@code Enum} types. + * @param identifier the identifier of hard-coded values for the metadata entity to be returned. + * @return an implementation of the required interface, or the code list element. + */ + @Override + public <T> T lookup(final Class<T> type, final String identifier) { + ArgumentChecks.ensureNonNull("type", type); + ArgumentChecks.ensureNonEmpty("identifier", identifier); + Object value; + if (ControlledVocabulary.class.isAssignableFrom(type)) { + value = getCodeList(type, identifier); + } else { + // TODO: move some code from ServicesForUtility here. + return NilReason.MISSING.createNilObject(type); + } + return type.cast(value); + } + + /** + * Ignored. + */ + @Override + public void addWarningListener(WarningListener<? super MetadataSource> listener) { + } + + /** + * Ignored. + */ + @Override + public void removeWarningListener(WarningListener<? super MetadataSource> listener) { + } + + /** + * Ignored. + */ + @Override + public void close() { + } +} Propchange: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataFallback.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataFallback.java ------------------------------------------------------------------------------ svn:mime-type = text/plain;charset=UTF-8 Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java?rev=1832046&r1=1832045&r2=1832046&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java [UTF-8] (original) +++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/sql/MetadataSource.java [UTF-8] Tue May 22 16:35:40 2018 @@ -41,6 +41,7 @@ import java.sql.Connection; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.SQLTransientException; import java.sql.SQLNonTransientException; import java.sql.PreparedStatement; import org.opengis.annotation.UML; @@ -106,7 +107,7 @@ import org.apache.sis.util.iso.Types; * * @author Touraïvane (IRD) * @author Martin Desruisseaux (IRD, Geomatys) - * @version 0.8 + * @version 1.0 * @since 0.8 * @module */ @@ -285,12 +286,16 @@ public class MetadataSource implements A /** * The instance connected to the {@code "jdbc/SpatialMetadata"} database, * created when first needed and cleared when the classpath change. + * May be {@link MetadataFallback#INSTANCE} if we failed to establish + * a connection to the database for a non-transient reason. */ - private static volatile MetadataSource instance; + private static MetadataSource instance; static { SystemListener.add(new SystemListener(Modules.METADATA) { @Override protected void classpathChanged() { - instance = null; + synchronized (MetadataSource.class) { + instance = null; + } } }); } @@ -302,28 +307,51 @@ public class MetadataSource implements A * citations} and {@linkplain org.apache.sis.metadata.iso.distribution.DefaultFormat formats} * among others. * + * <p>If connection to the metadata database can not be established, then this method returns + * a fallback with a few hard-coded values.</p> + * * @return source of pre-defined metadata records from the {@code "jdbc/SpatialMetadata"} database. - * @throws MetadataStoreException if this method can not connect to the database. */ - public static MetadataSource getProvided() throws MetadataStoreException { + public static synchronized MetadataSource getProvided() { MetadataSource ms = instance; if (ms == null) { - final DataSource dataSource; + LogRecord warning = null; + boolean isTransient = false; try { - dataSource = Initializer.getDataSource(); - } catch (Exception e) { - throw new MetadataStoreException(Errors.format(Errors.Keys.CanNotConnectTo_1, Initializer.JNDI), e); - } - if (dataSource == null) { - throw new MetadataStoreException(Initializer.unspecified(null)); - } - synchronized (MetadataSource.class) { - ms = instance; - if (ms == null) { + final DataSource dataSource = Initializer.getDataSource(); + if (dataSource != null) { ms = new MetadataSource(MetadataStandard.ISO_19115, dataSource, "metadata", null); ms.install(); - instance = ms; + } else { + warning = (LogRecord) Initializer.unspecified(null, true); + ms = MetadataFallback.INSTANCE; } + } catch (Exception e) { + ms = MetadataFallback.INSTANCE; + /* + * Derby sometime wraps SQLException into another SQLException. For making the stack strace a + * little bit simpler, keep only the root cause provided that the exception type is compatible. + */ + warning = Errors.getResources((Locale) null).getLogRecord(Level.WARNING, Errors.Keys.CanNotConnectTo_1, Initializer.JNDI); + warning.setThrown(Exceptions.unwrap(e)); + /* + * If the error is transient or has a transient cause, we will not save MetadataFallback.INSTANCE + * in the 'instance' field. The intent is to try again next time this method will be invoked, in + * case the transient error has disappeared. + */ + for (Throwable cause = e; cause != null; cause = cause.getCause()) { + if (cause instanceof SQLTransientException) { + isTransient = true; + break; + } + } + } + if (warning != null) { + warning.setLoggerName(Loggers.SYSTEM); + Logging.log(MetadataSource.class, "getProvided", warning); + } + if (!isTransient) { + instance = ms; } } return ms; @@ -399,6 +427,21 @@ public class MetadataSource implements A } /** + * For {@link MetadataFallback} constructor only. + */ + MetadataSource() { + standard = MetadataStandard.ISO_19115; + dataSource = null; + catalog = null; + statements = null; + tableColumns = null; + classloader = getClass().getClassLoader(); + pool = null; + lastUsed = null; + listeners = null; + } + + /** * If the metadata schema does not exist in the database, creates it and inserts the pre-defined metadata values. * The current implementation has the following restrictions: * @@ -410,32 +453,24 @@ public class MetadataSource implements A * Maintenance note: this method is invoked by reflection in {@code non-free:sis-embedded-data} module. * If we make this method public in a future Apache SIS version, then we can remove the reflection code. * - * @throws MetadataStoreException if an error occurred while inserting the metadata. + * @throws SQLException if an error occurred while inserting the metadata. */ - final synchronized void install() throws MetadataStoreException { - try { - final Connection connection = connection(); - final DatabaseMetaData md = connection.getMetaData(); - if (md.storesUpperCaseIdentifiers()) { - schema = schema.toUpperCase(Locale.US); - } else if (md.storesLowerCaseIdentifiers()) { - schema = schema.toLowerCase(Locale.US); - } - quoteSchema = false; - try (ResultSet result = md.getTables(catalog, schema, "CI_Citation", null)) { - if (result.next()) { - return; - } - } - final Installer installer = new Installer(connection); - installer.run(); - } catch (IOException | SQLException e) { - /* - * Derby sometime wraps SQLException into another SQLException. For making the stack strace a - * little bit simpler, keep only the root cause provided that the exception type is compatible. - */ - throw new MetadataStoreException(e.getLocalizedMessage(), Exceptions.unwrap(e)); + final synchronized void install() throws IOException, SQLException { + final Connection connection = connection(); + final DatabaseMetaData md = connection.getMetaData(); + if (md.storesUpperCaseIdentifiers()) { + schema = schema.toUpperCase(Locale.US); + } else if (md.storesLowerCaseIdentifiers()) { + schema = schema.toLowerCase(Locale.US); + } + quoteSchema = false; + try (ResultSet result = md.getTables(catalog, schema, "CI_Citation", null)) { + if (result.next()) { + return; + } } + final Installer installer = new Installer(connection); + installer.run(); } /** @@ -839,7 +874,7 @@ public class MetadataSource implements A final Class<?> subType = subType(type, identifier); final Dispatcher toSearch = new Dispatcher(identifier, this); try { - value = subType.newInstance(); + value = subType.getConstructor().newInstance(); final LookupInfo info = getLookupInfo(subType); final Map<String,Object> map = asValueMap(value); final Map<String,String> methods = standard.asNameMap(subType, NAME_POLICY, KeyNamePolicy.METHOD_NAME); @@ -984,7 +1019,7 @@ public class MetadataSource implements A * message when the actual class is unknown (it must have been checked dynamically by the caller however). */ @SuppressWarnings("unchecked") - private static ControlledVocabulary getCodeList(final Class<?> type, final String name) { + static ControlledVocabulary getCodeList(final Class<?> type, final String name) { if (type.isEnum()) { return (ControlledVocabulary) Types.forEnumName(type.asSubclass(Enum.class), name); } else { Modified: sis/branches/JDK8/core/sis-referencing-by-identifiers/pom.xml URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing-by-identifiers/pom.xml?rev=1832046&r1=1832045&r2=1832046&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-referencing-by-identifiers/pom.xml (original) +++ sis/branches/JDK8/core/sis-referencing-by-identifiers/pom.xml Tue May 22 16:35:40 2018 @@ -135,6 +135,11 @@ <type>test-jar</type> <scope>test</scope> </dependency> + <!-- dependency> + <groupId>org.apache.derby</groupId> + <artifactId>derby</artifactId> + <scope>test</scope> + </dependency --> </dependencies> </project> Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java?rev=1832046&r1=1832045&r2=1832046&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] (original) +++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] Tue May 22 16:35:40 2018 @@ -268,7 +268,7 @@ public class EPSGFactory extends Concurr if (ds == null) try { ds = Initializer.getDataSource(); if (ds == null) { - throw new UnavailableFactoryException(Initializer.unspecified(locale)); + throw new UnavailableFactoryException(String.valueOf(Initializer.unspecified(locale, false))); } } catch (Exception e) { throw new UnavailableFactoryException(canNotUse(e), e); Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/report/CoordinateReferenceSystems.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/report/CoordinateReferenceSystems.java?rev=1832046&r1=1832045&r2=1832046&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/report/CoordinateReferenceSystems.java [UTF-8] (original) +++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/report/CoordinateReferenceSystems.java [UTF-8] Tue May 22 16:35:40 2018 @@ -74,7 +74,7 @@ import static org.junit.Assert.*; * after any upgrade of the EPSG dataset.</p> * * @author Martin Desruisseaux (Geomatys) - * @version 0.7 + * @version 1.0 * @since 0.7 * @module */ @@ -480,7 +480,7 @@ public final strictfp class CoordinateRe properties.setProperty("PRODUCT.URL", "http://sis.apache.org"); properties.setProperty("JAVADOC.GEOAPI", "http://www.geoapi.org/snapshot/javadoc"); properties.setProperty("FACTORY.NAME", "EPSG"); - properties.setProperty("FACTORY.VERSION", "9.1"); + properties.setProperty("FACTORY.VERSION", "9.3"); properties.setProperty("FACTORY.VERSION.SUFFIX", ", together with other sources"); properties.setProperty("PRODUCT.VERSION.SUFFIX", " (provided that <a href=\"http://sis.apache.org/epsg.html\">a connection to an EPSG database exists</a>)"); properties.setProperty("DESCRIPTION", "<p><b>Notation:</b></p>\n" +