db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhille...@apache.org
Subject svn commit: r672019 - in /db/derby/code/trunk: java/tools/org/apache/derby/iapi/tools/run.java java/tools/org/apache/derby/loc/toolsmessages.properties java/tools/org/apache/derby/tools/SignatureChecker.java tools/jar/tools.properties
Date Thu, 26 Jun 2008 20:28:49 GMT
Author: rhillegas
Date: Thu Jun 26 13:28:49 2008
New Revision: 672019

URL: http://svn.apache.org/viewvc?rev=672019&view=rev
Log:
DERBY-3652: Add a lint tool, SignatureChecker, which checks whether the routines registered
in a database actually match static Java methods visible on the session's classpath.

Added:
    db/derby/code/trunk/java/tools/org/apache/derby/tools/SignatureChecker.java   (with props)
Modified:
    db/derby/code/trunk/java/tools/org/apache/derby/iapi/tools/run.java
    db/derby/code/trunk/java/tools/org/apache/derby/loc/toolsmessages.properties
    db/derby/code/trunk/tools/jar/tools.properties

Modified: db/derby/code/trunk/java/tools/org/apache/derby/iapi/tools/run.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/tools/org/apache/derby/iapi/tools/run.java?rev=672019&r1=672018&r2=672019&view=diff
==============================================================================
--- db/derby/code/trunk/java/tools/org/apache/derby/iapi/tools/run.java (original)
+++ db/derby/code/trunk/java/tools/org/apache/derby/iapi/tools/run.java Thu Jun 26 13:28:49
2008
@@ -26,6 +26,7 @@
 import org.apache.derby.tools.dblook;
 import org.apache.derby.tools.ij;
 import org.apache.derby.tools.sysinfo;
+import org.apache.derby.tools.SignatureChecker;
 import org.apache.derby.iapi.tools.i18n.LocalizedResource;
 
 /**
@@ -37,6 +38,7 @@
   java -jar derbyrun.jar sysinfo [-cp ...] [-cp help]<br>
   java -jar derbyrun.jar dblook [args] (or no arguments for usage)<br>
   java -jar derbyrun.jar server [args] (or no arguments for usage)<br>
+  java -jar derbyrun.jar SignatureChecker [args] (or no arguments for usage)<br>
 */
 public class run {
 
@@ -55,6 +57,8 @@
           dblook.main(trimArgs(args));
       } else if (args[0].equals("server")) {
           NetworkServerControl.main(trimArgs(args));
+      } else if (args[0].equals("SignatureChecker")) {
+          SignatureChecker.main(trimArgs(args));
       } else printUsage();
   }
 

Modified: db/derby/code/trunk/java/tools/org/apache/derby/loc/toolsmessages.properties
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/tools/org/apache/derby/loc/toolsmessages.properties?rev=672019&r1=672018&r2=672019&view=diff
==============================================================================
--- db/derby/code/trunk/java/tools/org/apache/derby/loc/toolsmessages.properties (original)
+++ db/derby/code/trunk/java/tools/org/apache/derby/loc/toolsmessages.properties Thu Jun 26
13:28:49 2008
@@ -265,3 +265,18 @@
 java -jar derbyrun.jar sysinfo [-cp ...] [-cp help] \n\
 java -jar derbyrun.jar dblook [args] (or no arguments for usage)\n\
 java -jar derbyrun.jar server [args] (or no arguments for usage)
+
+#
+# Messages for SignatureChecker.java
+#
+SC_USAGE=Usage if running on J2SE:\n\
+\ \n\
+\ \tjava org.apache.derby.tools.SignatureChecker  CONNECTION_URL_TO_DATABASE\n\
+\ \n\
+Usage if running on J2ME:\n\
+\ \n\
+\ \tjava org.apache.derby.tools.SignatureChecker DATABASE_NAME
+SC_NO_CONN=Could not get a connection.
+SC_FOUND_MATCH=Found a matching method for: {0}
+SC_UNRESOLVABLE=Unresolvable routine: {0}. Detailed reason: {1}
+

Added: db/derby/code/trunk/java/tools/org/apache/derby/tools/SignatureChecker.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/tools/org/apache/derby/tools/SignatureChecker.java?rev=672019&view=auto
==============================================================================
--- db/derby/code/trunk/java/tools/org/apache/derby/tools/SignatureChecker.java (added)
+++ db/derby/code/trunk/java/tools/org/apache/derby/tools/SignatureChecker.java Thu Jun 26
13:28:49 2008
@@ -0,0 +1,684 @@
+/*
+
+   Derby - Class org.apache.derby.tools.SignatureChecker
+
+   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.derby.tools;
+
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+
+import org.apache.derby.iapi.services.info.JVMInfo;
+import org.apache.derby.jdbc.EmbeddedSimpleDataSource;
+import org.apache.derby.shared.common.reference.JDBC40Translation;
+import org.apache.derby.iapi.tools.i18n.LocalizedResource;
+
+/**
+   <p>
+   This class shows which user declared SQL functions and procedures
+   cannot be matched with Java methods.
+   </p>
+
+   <p>
+   To run from the command-line, enter the following if running on J2SE:
+   </p>
+   
+    <p>
+	<code>java org.apache.derby.tools.SignatureChecker CONNECTION_URL_TO_DATABASE</code>
+	<p>
+
+   <p>
+   And enter the following if running on J2ME:
+   </p>
+   
+    <p>
+	<code>java org.apache.derby.tools.SignatureChecker DATABASE_NAME</code>
+	<p>
+
+*/
+
+public class SignatureChecker
+{
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTANTS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    private static  final   String  WILDCARD = "%";
+
+    private static  final   String[] SYSTEM_SCHEMAS =
+    {
+        "SQLJ",
+        "SYSCS_UTIL",
+        "SYSIBM",
+    };
+
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // STATE
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    private ParsedArgs _parsedArgs;
+
+    private ArrayList   _procedures = new ArrayList();
+    private ArrayList   _functions = new ArrayList();
+
+    private boolean     _debugging = false;
+    
+	private static          LocalizedResource _messageFormatter;
+    
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTRUCTOR
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    private SignatureChecker( ParsedArgs parsedArgs )
+    {
+        _parsedArgs = parsedArgs;
+    }
+    
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // ENTRY POINT
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    public  static  void    main( String[] args )
+    {
+        ParsedArgs  parsedArgs = new ParsedArgs( args );
+        
+        if ( !parsedArgs.isValid() )
+        {
+            printUsage();
+            System.exit( 1 );
+        }
+        else
+        {
+            SignatureChecker    me = new SignatureChecker(  parsedArgs  );
+
+            me.execute();
+        }
+    }
+    
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // MACHINERY
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * Get a connection to a database and then match the signatures of routines
+     * in that database.
+     * </p>
+     */
+    private void    execute()
+    {
+        try {
+            Connection  conn;
+        
+            if ( _parsedArgs.isJ2ME() ) { conn = getJ2MEConnection(); }
+            else { conn = getJ2SEConnection(); }
+
+            if ( conn == null )
+            {
+                println(  formatMessage( "SC_NO_CONN" )  );
+                return;
+            }
+            else
+            {
+                matchSignatures( conn );
+
+                conn.close();
+            }
+            
+        } catch (Throwable t) { printThrowable( t ); }
+    }
+
+    /**
+     * <p>
+     * Match the signatures of routines in the database attached to this connection.
+     * </p>
+     */
+    private void matchSignatures( Connection conn )
+        throws SQLException
+    {
+        matchProcedures( conn );
+        matchFunctions( conn );
+    }
+    
+    /**
+     * <p>
+     * Match the signatures of procedures in this database.
+     * </p>
+     */
+    private void matchProcedures( Connection conn )
+        throws SQLException
+    {
+        DatabaseMetaData    dbmd = conn.getMetaData();
+
+        // find all of the user-declared procedures
+        findProcedures( dbmd );
+
+        // for each procedure, count its arguments
+        countProcedureArgs( dbmd );
+
+        //
+        // Try to prepare an invocation of each procedure. This will generate an error if
a
+        // matching Java signature can not be found.
+        //
+        int     count = _procedures.size();
+        for ( int i = 0; i < count; i++ )
+        {
+            SQLRoutine  procedure = getProcedure( i );
+            StringBuffer buffer = new StringBuffer();
+            int             argCount = procedure.getArgCount();
+
+            buffer.append( "call " );
+            buffer.append( procedure.getQualifiedName() );
+            buffer.append( "( " );
+            for ( int k = 0; k < argCount; k++ )
+            {
+                if ( k > 0 ) { buffer.append( ", " ); }
+                buffer.append( " ? " );
+            }
+            buffer.append( " )" );
+
+            checkSignature( conn, procedure, buffer.toString(), makeReadableSignature( procedure
) );
+        }
+    }
+
+    /**
+     * <p>
+     * Match the signatures of functions in this database.
+     * </p>
+     */
+    private void matchFunctions( Connection conn )
+        throws SQLException
+    {
+        DatabaseMetaData    dbmd = conn.getMetaData();
+
+        // find all of the user-declared functions
+        findFunctions( dbmd );
+
+        // for each function, count its arguments
+        countFunctionArgs( dbmd );
+
+        //
+        // Try to prepare an invocation of each function. This will generate an error if
a
+        // matching Java signature can not be found.
+        //
+        int     count = _functions.size();
+        for ( int i = 0; i < count; i++ )
+        {
+            SQLRoutine  function = getFunction( i );
+            StringBuffer query = new StringBuffer();
+            int             argCount = function.getArgCount();
+
+            if ( function.isTableFunction() ) { query.append( "select * from table( " );
}
+            else { query.append( "values(  " ); }
+            
+            query.append( function.getQualifiedName() );
+            query.append( "( " );
+            for ( int k = 0; k < argCount; k++ )
+            {
+                if ( k > 0 ) { query.append( ", " ); }
+                query.append( " ? " );
+            }
+            query.append( " ) )" );
+            if ( function.isTableFunction() ) { query.append( " s" ); }
+
+            checkSignature( conn, function, query.toString(), makeReadableSignature( function
) );
+        }
+    }
+
+    /**
+     * <p>
+     * Make a human readable signature for a routine. This can be
+     * used in error messages.
+     * </p>
+     */
+    private String  makeReadableSignature( SQLRoutine routine )
+    {
+        StringBuffer signature = new StringBuffer();
+        int             argCount = routine.getArgCount();
+        
+        signature.append( routine.getQualifiedName() );
+        signature.append( "( " );
+        for ( int k = 0; k < argCount; k++ )
+        {
+            if ( k > 0 ) { signature.append( ", " ); }
+            signature.append( " " + routine.getArgType( k ) + " " );
+        }
+        signature.append( " )" );
+
+        return signature.toString();
+    }
+
+    /**
+     * <p>
+     * Find all of the user-declared procedures.
+     * </p>
+     */
+    private void    findProcedures( DatabaseMetaData dbmd )
+        throws SQLException
+    {
+        ResultSet               rs = dbmd.getProcedures( null, null, WILDCARD );
+
+        while( rs.next() )
+        {
+            String  schema = rs.getString( 2 );
+            String  name = rs.getString( 3 );
+
+            if ( isSystemSchema( schema ) ) { continue; }
+
+            putProcedure( schema, name );
+        }
+        rs.close();
+    }
+    
+    /**
+     * <p>
+     * Count up the arguments to the user-coded procedures.
+     * </p>
+     */
+    private void    countProcedureArgs( DatabaseMetaData dbmd )
+        throws SQLException
+    {
+        int     count = _procedures.size();
+        for ( int i = 0; i < count; i++ )
+        {
+            SQLRoutine  procedure = getProcedure( i );
+
+            ResultSet   rs = dbmd.getProcedureColumns( null, procedure.getSchema(), procedure.getName(),
WILDCARD );
+
+            while( rs.next() )
+            {
+                procedure.addArg( rs.getString( 7 ) );
+            }
+            rs.close();
+        }
+    }
+    
+    /**
+     * <p>
+     * Find all of the user-declared functions. We use reflection to get our
+     * hands on getFunctions() because that method does not appear in
+     * the JSR169 api for DatabaseMetaData.
+     * </p>
+     */
+    private void    findFunctions( DatabaseMetaData dbmd )
+        throws SQLException
+    {
+        try {
+            Method      getFunctionsMethod = dbmd.getClass().getMethod
+                ( "getFunctions", new Class[] { String.class, String.class, String.class
} );
+            ResultSet   rs = (ResultSet) getFunctionsMethod.invoke
+                ( dbmd, new Object[] { null, null, WILDCARD } );
+
+            while( rs.next() )
+            {
+                String  schema = rs.getString( 2 );
+                String  name = rs.getString( 3 );
+                short   functionType = rs.getShort( 5 );
+
+                if ( isSystemSchema( schema ) ) { continue; }
+
+                boolean isTableFunction;
+                if ( functionType == JDBC40Translation.FUNCTION_RETURNS_TABLE ) { isTableFunction
= true; }
+                else { isTableFunction = false; }
+
+                putFunction( schema, name, isTableFunction );
+            }
+            rs.close();
+
+            
+        } catch (Exception e) { throw new SQLException( e.getMessage() ); }
+    }
+
+    /**
+     * <p>
+     * Count up the arguments to the user-coded procedures. We use
+     * reflection to look up the getFunctionColumns() method because that
+     * method does not appear in the JSR169 api for DatabaseMetaData.
+     * </p>
+     */
+    private void    countFunctionArgs( DatabaseMetaData dbmd )
+        throws SQLException
+    {
+        try {
+            Method      getFunctionColumnsMethod = dbmd.getClass().getMethod
+                ( "getFunctionColumns", new Class[] { String.class, String.class, String.class,
String.class } );
+
+            int     count = _functions.size();
+            for ( int i = 0; i < count; i++ )
+            {
+                SQLRoutine  function = getFunction( i );
+
+                ResultSet   rs = (ResultSet) getFunctionColumnsMethod.invoke
+                    ( dbmd, new Object[] { null, function.getSchema(), function.getName(),
WILDCARD } );
+
+                while( rs.next() )
+                {
+                    short   columnType = rs.getShort( 5 );
+
+                    //
+                    // Skip the return value if this is a table function.
+                    // Skip all columns in the returned result set if this is a
+                    // table function.
+                    //
+                    if ( columnType == JDBC40Translation.FUNCTION_RETURN ) { continue; }
+                    if ( columnType == JDBC40Translation.FUNCTION_COLUMN_RESULT ) { continue;
}
+                    
+                    function.addArg( rs.getString( 7 ) );
+                }
+                rs.close();
+            }
+        } catch (Exception e) { throw new SQLException( e.getMessage() ); }
+        
+    }
+    
+    /**
+     * <p>
+     * Prepared a routine invocation in order to check whether it matches a Java method.
+     * </p>
+     */
+    private void    checkSignature( Connection conn, SQLRoutine routine, String query, String
readableSignature )
+    {
+        try {
+            PreparedStatement   ps = prepareStatement( conn, query );
+            ps.close();
+
+            println( formatMessage( "SC_FOUND_MATCH", readableSignature ) );
+
+                     } catch (SQLException se)
+        {
+            println( formatMessage( "SC_UNRESOLVABLE", readableSignature, se.getMessage()
) );
+        }
+    }
+    
+    
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // MINIONS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    private Connection  getJ2MEConnection()
+        throws SQLException
+    {
+        EmbeddedSimpleDataSource    dataSource = new EmbeddedSimpleDataSource();
+
+        dataSource.setDatabaseName( _parsedArgs.getJ2meDatabaseName() );
+
+        return dataSource.getConnection();
+    }
+
+    /**
+     * We use reflection to get the J2SE connection so that references to
+     * DriverManager will not generate linkage errors on old J2ME platforms
+     * which may resolve references eagerly.
+     */
+    private Connection  getJ2SEConnection()
+        throws SQLException
+    {
+        try {
+            Class.forName( "org.apache.derby.jdbc.EmbeddedDriver" );
+            Class.forName( "org.apache.derby.jdbc.ClientDriver" );
+        } catch (Throwable t) {}
+
+        try {
+            Class   driverManagerClass = Class.forName( "java.sql.DriverManager" );
+            Method  getConnectionMethod = driverManagerClass.getDeclaredMethod
+                ( "getConnection", new Class[] { String.class } );
+
+            return (Connection) getConnectionMethod.invoke
+                ( null, new Object[] { _parsedArgs.getJ2seConnectionUrl() } );
+            
+        } catch (Throwable t)
+        {
+            printThrowable( t );
+            return null;
+        }
+    }
+    
+    private PreparedStatement prepareStatement( Connection conn, String text )
+        throws SQLException
+    {
+        if ( _debugging ) { println( "Preparing: " + text ); }
+
+        return conn.prepareStatement( text );
+    }
+
+    private static  void printUsage()
+    {
+        println(  formatMessage( "SC_USAGE" )  );
+    }
+
+    private static void printThrowable( Throwable t )
+    {
+        t.printStackTrace();
+    }
+    
+    private static  void println( String text )
+    {
+        System.out.println( text );
+    }
+
+    /**
+     * Return true if the schema is a system schema.
+     */
+    private boolean isSystemSchema( String schema )
+    {
+        int count = SYSTEM_SCHEMAS.length;
+
+        for ( int i = 0; i < count; i++ )
+        {
+            if ( SYSTEM_SCHEMAS[ i ].equals( schema ) ) { return true; }
+        }
+
+        return false;
+    }
+    
+    /**
+     * Store a procedure descriptor.
+     */
+    private void putProcedure( String schema, String name )
+    {
+        _procedures.add( new SQLRoutine( schema, name, false )  );
+    }
+        
+    /**
+     * Get a procedure descriptor.
+     */
+    private SQLRoutine getProcedure( int idx )
+    {
+        return (SQLRoutine) _procedures.get( idx );
+    }
+
+    /**
+     * Store a function descriptor.
+     */
+    private void putFunction( String schema, String name, boolean isTableFunction )
+    {
+        _functions.add( new SQLRoutine( schema, name, isTableFunction )  );
+    }
+        
+    /**
+     * Get a functon descriptor.
+     */
+    private SQLRoutine getFunction( int idx )
+    {
+        return (SQLRoutine) _functions.get( idx );
+    }
+
+    /**
+     * Format a localizable message with 0 args.
+     */
+    private static  String  formatMessage( String key )
+    {
+        return getMessageFormatter().getTextMessage( key );
+    }
+    
+    /**
+     * Format a localizable message with 1 arg.
+     */
+    private static  String  formatMessage( String key, String arg0 )
+    {
+        return getMessageFormatter().getTextMessage( key, arg0 );
+    }
+    
+    /**
+     * Format a localizable message with 2 args.
+     */
+    private static  String  formatMessage( String key, String arg0, String arg1 )
+    {
+        return getMessageFormatter().getTextMessage( key, arg0, arg1 );
+    }
+    
+    /**
+     * Get the message resource.
+     */
+    private static  LocalizedResource   getMessageFormatter()
+    {
+        if ( _messageFormatter == null )
+        {
+            _messageFormatter = LocalizedResource.getInstance();
+        }
+        return _messageFormatter;
+    }
+
+
+        
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // INNER CLASSES
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    static class ParsedArgs
+    {
+        private boolean _isValid;
+        private boolean _isJ2ME;
+        private String _j2seConnectionUrl;
+        private String _j2meDatabaseName;
+
+        public  ParsedArgs( String[] args )
+        {
+            _isValid = false;
+            
+            _isJ2ME = !classExists( "java.sql.DriverManager" );
+
+            parseArgs( args );
+        }
+
+        public boolean isValid() { return _isValid; }
+        public boolean isJ2ME() { return _isJ2ME; }
+
+        public String getJ2seConnectionUrl() { return _j2seConnectionUrl; }
+        public String getJ2meDatabaseName() { return _j2meDatabaseName; }
+
+        private void parseArgs( String[] args )
+        {
+            if ( args == null ) { return; }
+            if ( args.length == 0 ) { return; }
+            
+            if ( isJ2ME() )
+            {
+                if ( args.length != 1 ) { return; }
+
+                _j2meDatabaseName = args[ 0 ];
+                _isValid = true;
+            }
+            else
+            {
+                if ( args.length != 1 ) { return; }
+
+                _j2seConnectionUrl = args[ 0 ];
+                _isValid = true;
+            }
+        }
+
+        private boolean classExists( String className )
+        {
+            try {
+                Class.forName( className );
+
+                return true;
+            } catch (Throwable t) { return false; }
+        }
+    }
+
+    class SQLRoutine
+    {
+        private String _schema;
+        private String _name;
+        private boolean _isTableFunction;
+        private ArrayList   _argList = new ArrayList();
+
+        public SQLRoutine( String schema, String name, boolean isTableFunction )
+        {
+            _schema = schema;
+            _name = name;
+            _isTableFunction = isTableFunction;
+        }
+
+        public void addArg( String typeName ) { _argList.add( typeName ); }
+
+        public String getSchema() { return _schema; }
+        public String getName() { return _name; }
+        public int      getArgCount() { return _argList.size(); }
+        public String getArgType( int idx ) { return (String) _argList.get( idx ); }
+        public boolean isTableFunction() { return _isTableFunction; }
+
+        public  String  toString()
+        {
+            StringBuffer    buffer = new StringBuffer();
+
+            buffer.append( "SQLRoutine( " );
+            buffer.append( _schema + ", " );
+            buffer.append( _name + ", " );
+            buffer.append(  "isTableFunction = " + _isTableFunction + ", " );
+            buffer.append( " argCount = " + getArgCount() );
+            buffer.append( " )" );
+
+            return buffer.toString();
+        }
+
+        private String doubleQuote( String raw )
+        {
+            return '\"' + raw + '\"';
+        }
+        public  String  getQualifiedName()
+        {
+            return doubleQuote( _schema ) + '.' + doubleQuote( _name );
+        }
+
+    }
+    
+}

Propchange: db/derby/code/trunk/java/tools/org/apache/derby/tools/SignatureChecker.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/tools/jar/tools.properties
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/tools/jar/tools.properties?rev=672019&r1=672018&r2=672019&view=diff
==============================================================================
--- db/derby/code/trunk/tools/jar/tools.properties (original)
+++ db/derby/code/trunk/tools/jar/tools.properties Thu Jun 26 13:28:49 2008
@@ -27,3 +27,4 @@
 derby.module.Attribute=org.apache.derby.iapi.reference.Attribute
 derby.module.cslook=org.apache.derby.tools.dblook
 derby.module.sysinfo=org.apache.derby.tools.sysinfo
+derby.module.SignatureChecker=org.apache.derby.tools.SignatureChecker



Mime
View raw message