Simple Resource Clean-up

Roger Hernandez rhvarona at gmail.com
Tue Mar 3 00:21:19 PST 2009


The code fragment:

class ClassWithClose {
   public ClassWithClose () {
     ....
   }

  public void close () throws SQLException {
     if (cond) {
       throw SQLException("message");
     }
  }
}


try (ClassWithClose val = new ClassWithClose()) {
   funcThatThrowsIOException();
}

behaves exactly like:

ClassWithClose val = new ClassWithClose();
try {
   funcThatThrowsIOException();
} finally {
   val.close();
}

I am going to see the SQLException being thrown out.
On Tue, Mar 3, 2009 at 2:54 AM, Neal Gafter <neal at gafter.com> wrote:

> Just to make sure I understand, in your specification, if an exception
> is thrown from the try block, and then another exception is thrown
> from the close() method, the one from the close() is the one that
> propogates out?
>
> On Mon, Mar 2, 2009 at 11:43 PM, Roger Hernandez <rhvarona at gmail.com>
> wrote:
> > Sorry I meant to take that part out, since I saw the comments from the
> > previous proposal.  The way I envision it working is in a way how C++
> > template instantiation works.  The compiler just requires that a class
> have
> > an accessible "close()" function, it should not care what interface or
> class
> > it comes from.  That way it can throw any exception type.
> > - Show quoted text -
> >
> > On Tue, Mar 3, 2009 at 2:23 AM, Neal Gafter <neal at gafter.com> wrote:
> >>
> >> You say "The object being created must implement the Closeable
> >> interface."  But java.sql.Statement, to pick one example, cannot be
> >> made to implement that interface because the exception signatures are
> >> not compatible.
> >>
> >> I'll let others comment about issues with exception handling in the
> >> translation.
> >>
> >> -Neal
> >>
> >> On Mon, Mar 2, 2009 at 9:02 PM, Roger Hernandez <rhvarona at gmail.com>
> >> wrote:
> >> > I saw how the previous Automatic Resource Management proposal got torn
> >> > to
> >> > pieces but I feel strongly enough about this issue to post a similar
> >> > proposal.
> >> >
> >> > Some points:
> >> >
> >> > I am not a programming language designer.  I have very little
> experience
> >> > with byte code and with design and implementation of compilers.  What
> I
> >> > do
> >> > have is many years of experience in writing code for large business
> >> > applications, both in house-custom programs and shrink-wrapped
> products
> >> > sold
> >> > to customers.
> >> >
> >> > About 1/3 of the Java code I write contributes almost nothing to the
> >> > functionality or flexibility of the program.  It is composed of very
> >> > simple
> >> > and very repetitive resource clean-up code.  JDBC code is an
> especially
> >> > bad
> >> > case.  I know we should be using JDO or JPA, but: 1) most of the Java
> >> > database code in existence is using JDBC to some extent or another, 2)
> >> > for
> >> > the kind of processing our software does with large datasets having
> >> > hundreds
> >> > of megabytes of row data and millions of rows per account per month,
> >> > custom
> >> > JDBC code still beats the other solutions in memory utilization and
> >> > throughput.
> >> >
> >> > In most business code we don't do complex exception processing.  If we
> >> > get a
> >> > exception we rollback the transaction and unroll the stack until we
> >> > reach
> >> > some program level error handler that logs the error for some
> >> > administrator
> >> > to review at a later date.  So if this proposal is not applicable to
> >> > complex
> >> > error handling scenarios, that is fine.  Taking care of the simple
> >> > scenarios
> >> > will still get rid of most of that 1/3 of the code I write, allowing
> me
> >> > to
> >> > concentrate on the actual program logic, not the resource clean-up
> >> > noise.
> >> >
> >> > I also program quite a bit in C++ and C# and when I work in Java I
> >> > sorely
> >> > miss RAII (Resource Acquisition Is Initialization) and the "using"
> >> > statement
> >> > respectively.
> >> >
> >> > At the end of the day, what I would like is a solution to minimize all
> >> > the
> >> > resource clean-up boiler plate.
> >> >
> >> >
> >> >
> -----------------------------------------------------------------------------------------
> >> > PROJECT COIN SMALL LANGUAGE CHANGE PROPOSAL FORM v1.0
> >> >
> >> >   AUTHOR: Roger Hernandez, rogerh at velocityconsultinginc.com
> >> >
> >> > OVERVIEW
> >> >
> >> >   FEATURE SUMMARY: Syntactic sugar for simple cases of the common
> >> > new/try/finally/close language idiom.  The object being created must
> >> > implement the Closeable interface.
> >> >
> >> >   MAJOR ADVANTAGE: Significantly reduces lines of code when working
> with
> >> > objects that encapsulate external resources that should be closed as
> >> > soon as
> >> > possible.  Examples are Stream, Reader, Writer classes in java.io,
> and
> >> > Connection, Statement, PreparedStatement, ResultSet in java.sql.*.
> >> >
> >> >   MAJOR BENEFIT: It allows writing code that uses these kinds of
> object
> >> > to
> >> > more clearly express the both the lifetime of the utilization of each
> >> > resource, and allows the logic flow of the code to be more visible.
> >> >
> >> >   MAJOR DISADVANTAGE: Either a new keyword, or an additional
> overloaded
> >> > meaning on an existing keyword.
> >> >
> >> >   ALTERNATIVES: You can always use the standard idiom: SomeType val =
> >> > new
> >> > val(...); try { ... } finally { val.close(); }
> >> >
> >> > EXAMPLES
> >> >
> >> >   SIMPLE EXAMPLE:  A simple Java version of the command line utility
> >> > "tee".
> >> >      //This is the existing way of doing it.
> >> >      //Lines of code: 19
> >> >      package com.vci.projectcoin.using;
> >> >
> >> >      import java.io.*;
> >> >
> >> >      public class SimpleExample {
> >> >
> >> >         public static void main(String[] args) throws IOException {
> >> >            byte []buffer = new byte[1024];
> >> >            FileOutputStream out = new FileOutputStream(args[0]);
> >> >            try {
> >> >               for (int count; (count = System.in.read(buffer)) != -1;)
> {
> >> >                  out.write(buffer, 0, count);
> >> >                  System.out.write(buffer, 0, count);
> >> >               }
> >> >            } finally {
> >> >               out.close();
> >> >            }
> >> >         }
> >> >      }
> >> >
> >> >      //This is the proposed way of doing it, the compiler converts the
> >> > syntactic sugar into the same byte codes
> >> >      //I am adding a new use to the the "try" keyword to avoid adding
> >> > more
> >> > to the language, but it would work
> >> >      //just a well with a "using" keyword.
> >> >      //Lines of code: 16
> >> >      package com.vci.projectcoin.using;
> >> >
> >> >      import java.io.*;
> >> >
> >> >      public class SimpleExample {
> >> >
> >> >         public static void main(String[] args) throws IOException {
> >> >            byte []buffer = new byte[1024];
> >> >            try (FileOutputStream out = new FileOutputStream(args[0]))
> {
> >> >               for (int count; (count = System.in.read(buffer)) != -1;)
> {
> >> >                  out.write(buffer, 0, count);
> >> >                  System.out.write(buffer, 0, count);
> >> >               }
> >> >            }
> >> >         }
> >> >      }
> >> >
> >> >   ADVANCED EXAMPLE: A simple utility to execute a query and write
> values
> >> > as
> >> > a comma delimited file.
> >> >
> >> >      //This is the existing way of doing it
> >> >      //Lines of code: 55
> >> >      package com.vci.projectcoin.using;
> >> >
> >> >      import java.sql.*;
> >> >      import java.io.*;
> >> >
> >> >      public class AdvancedExample {
> >> >         final static String EOL =
> System.getProperty("line.separator");
> >> >
> >> >         //Command Line: <JDBC url> <query sql> [<output file>]
> >> >         static public void main(String []args) throws SQLException,
> >> > FileNotFoundException {
> >> >            String url = args[0];
> >> >            String sql = args[1];
> >> >            PrintWriter out = new PrintWriter(args.length > 2 ? new
> >> > FileOutputStream(args[2]) : System.out);
> >> >            try {
> >> >               Connection conn = DriverManager.getConnection(url);
> >> >               try {
> >> >                  Statement query =
> >> > conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
> >> > ResultSet.CONCUR_READ_ONLY);
> >> >                  try {
> >> >                     ResultSet results = query.executeQuery(sql);
> >> >                     try {
> >> >                        ResultSetMetaData meta = results.getMetaData();
> >> >                        int colCount = meta.getColumnCount();
> >> >                        while (results.next()) {
> >> >                           for (int index = 1; index <= colCount;
> >> > index++) {
> >> >                              int colType = meta.getColumnType(index);
> >> >                              boolean quoted = colType == Types.CHAR
>  ||
> >> > colType == Types.LONGNVARCHAR || colType == Types.LONGVARCHAR ||
> >> >                                               colType == Types.NCHAR
> ||
> >> > colType == Types.NVARCHAR     || colType == Types.VARCHAR;
> >> >                              if (quoted) {
> >> >                                 System.out.append('"');
> >> >                              }
> >> >
> >> >  System.out.append(results.getString(index));
> >> >                              if (quoted) {
> >> >                                 System.out.append('"');
> >> >                              }
> >> >                              if (index < colCount) {
> >> >                                 System.out.print(',');
> >> >                              } else {
> >> >                                 System.out.print(EOL);
> >> >                              }
> >> >                           }
> >> >                        }
> >> >                     } finally {
> >> >                        results.close();
> >> >                     }
> >> >                  } finally {
> >> >                     query.close();
> >> >                  }
> >> >               } finally {
> >> >                  conn.close();
> >> >               }
> >> >            } finally {
> >> >               out.close();
> >> >            }
> >> >         }
> >> >      }
> >> >
> >> >      //This is the proposed way of doing it
> >> >      //This proposal gets rid of the finally clean up per object.  It
> >> > lets
> >> > one write robust resource clean-up code without a lot of effort.
> >> >      //Lines of code: 43
> >> >      package com.vci.projectcoin.using;
> >> >
> >> >      import java.sql.*;
> >> >      import java.io.*;
> >> >
> >> >      public class AdvancedExample {
> >> >         final static String EOL =
> System.getProperty("line.separator");
> >> >
> >> >         //Command Line: <JDBC url> <query sql> [<output file>]
> >> >         static public void main(String []args) throws SQLException,
> >> > FileNotFoundException {
> >> >            String url = args[0];
> >> >            String sql = args[1];
> >> >            try (PrintWriter out = new PrintWriter(args.length > 2 ?
> new
> >> > FileOutputStream(args[2]) : System.out)) {
> >> >               try (Connection conn = DriverManager.getConnection(url))
> {
> >> >                  try (Statement query =
> >> > conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
> >> > ResultSet.CONCUR_READ_ONLY)) {
> >> >                     try (ResultSet results = query.executeQuery(sql))
> {
> >> >                        ResultSetMetaData meta = results.getMetaData();
> >> >                        int colCount = meta.getColumnCount();
> >> >                        while (results.next()) {
> >> >                           for (int index = 1; index <= colCount;
> >> > index++) {
> >> >                              int colType = meta.getColumnType(index);
> >> >                              boolean quoted = colType == Types.CHAR ||
> >> > colType == Types.LONGNVARCHAR || colType == Types.LONGVARCHAR ||
> colType
> >> > ==
> >> > Types.NCHAR || colType == Types.NVARCHAR || colType == Types.VARCHAR;
> >> >                              if (quoted) {
> >> >                                 System.out.append('"');
> >> >                              }
> >> >
> >> >  System.out.append(results.getString(index));
> >> >                              if (quoted) {
> >> >                                 System.out.append('"');
> >> >                              }
> >> >                              if (index < colCount) {
> >> >                                 System.out.print(',');
> >> >                              } else {
> >> >                                 System.out.print(EOL);
> >> >                              }
> >> >                           }
> >> >                           System.out.println();
> >> >                        }
> >> >                     }
> >> >                  }
> >> >               }
> >> >            }
> >> >         }
> >> >      }
> >> >
> >> >      //This is an additional syntactic sugar proposal, allowing
> multiple
> >> > objects to be allocated inside one try block.  The compiler converts
> all
> >> > three programs into the same bytecode
> >> >      //This proposal gets rid of the additional indentation level and
> >> > closing brace per object.  It further minimize the clean-up
> >> > boiler-plate,
> >> > allowing the point of the program logic to be clearer.
> >> >      //Lines of code: 38
> >> >      package com.vci.projectcoin.using;
> >> >
> >> >      import java.sql.*;
> >> >      import java.io.*;
> >> >
> >> >      public class AdvancedExample {
> >> >         final static String EOL =
> System.getProperty("line.separator");
> >> >
> >> >         //Command Line: <JDBC url> <query sql> [<output file>]
> >> >         static public void main(String []args) throws SQLException,
> >> > FileNotFoundException {
> >> >            String url = args[0];
> >> >            String sql = args[1];
> >> >            try (PrintWriter out = new PrintWriter(args.length > 2 ?
> new
> >> > FileOutputStream(args[2]) : System.out),
> >> >                 Statement query =
> >> > conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
> >> > ResultSet.CONCUR_READ_ONLY),
> >> >                 ResultSet results = query.executeQuery(sql)) {
> >> >               ResultSetMetaData meta = results.getMetaData();
> >> >               int colCount = meta.getColumnCount();
> >> >               while (results.next()) {
> >> >               for (int index = 1; index <= colCount; index++) {
> >> >                  int colType = meta.getColumnType(index);
> >> >                  boolean quoted = colType == Types.CHAR  || colType ==
> >> > Types.LONGNVARCHAR || colType == Types.LONGVARCHAR || colType ==
> >> > Types.NCHAR
> >> > || colType == Types.NVARCHAR     || colType == Types.VARCHAR;
> >> >                  if (quoted) {
> >> >                     System.out.append('"');
> >> >                  }
> >> >                  System.out.append(results.getString(index));
> >> >                  if (quoted) {
> >> >                     System.out.append('"');
> >> >                  }
> >> >                  if (index < colCount) {
> >> >                     System.out.print(',');
> >> >                  } else {
> >> >                     System.out.print(EOL);
> >> >                  }
> >> >               }
> >> >               System.out.println();
> >> >               }
> >> >            }
> >> >         }
> >> >      }
> >> >
> >> > DETAILS
> >> >   The specification requires that the object in the try () block have
> a
> >> > "close()" method.  Wether the method throws any or no exception, or if
> >> > it
> >> > returns a value or no value does not matter.  The proposal is not
> trying
> >> > to
> >> > introduce any new intelligence into the try finally clause, it is just
> >> > syntactic sugar to minimize simple resource clean-up code.
> >> >
> >> >   SPECIFICATION:
> >> >      The "try" keyword will have an overloaded meaning
> >> >
> >> >      CASE 1
> >> >      ------
> >> >      try (ClassWithCloseMethod value = new ClassWithCloseMethod(...))
> {
> >> >         //work gets done here
> >> >      }
> >> >
> >> >      Will be syntactic sugar for:
> >> >      ClassWithCloseMethod value = new ClassWithCloseMethod(...);
> >> >      try {
> >> >         //work gets done here
> >> >      } finally {
> >> >         value.close();
> >> >      }
> >> >
> >> >      CASE 2
> >> >      ------
> >> >      try (ClassWithCloseMethod value = new ClassWithCloseMethod(...))
> {
> >> >         //work gets done here
> >> >      } finally {
> >> >         //additional clean-up code
> >> >      }
> >> >
> >> >      Will be syntactic sugar for:
> >> >      ClassWithCloseMethod value = new ClassWithCloseMethod(...);
> >> >      try {
> >> >         //work gets done here
> >> >      } finally {
> >> >         value.close();
> >> >         //additional clean-up code
> >> >      }
> >> >
> >> >      CASE 3
> >> >      ------
> >> >      try (ClassWithCloseMethod value = new ClassWithCloseMethod(...))
> {
> >> >         //work gets done here
> >> >      } catch (Exception ex) {
> >> >         //exception handling code
> >> >      } finally {
> >> >         //additional clean-up code
> >> >      }
> >> >
> >> >      Will be syntactic sugar for:
> >> >      ClassWithCloseMethod value = new ClassWithCloseMethod(...);
> >> >      try {
> >> >         //work gets done here
> >> >      } catch (Exception ex) {
> >> >         //exception handling code
> >> >      } finally {
> >> >         value.close();
> >> >         //additional clean-up code
> >> >      }
> >> >
> >> >      CASE 4
> >> >      ------
> >> >      try (Class1WithCloseMethod value1 = new
> Class1WithCloseMethod(...),
> >> >           Class2WithCloseMethod value2 = new
> Class2WithCloseMethod(...),
> >> >           Class3WithCloseMethod value3 = new
> Class3WithCloseMethod(...))
> >> > {
> >> >         //work gets done here
> >> >      }
> >> >
> >> >      Will be syntactic sugar for:
> >> >      Class1WithCloseMethod value1 = new Class1WithCloseMethod(...);
> >> >      try {
> >> >         Class2WithCloseMethod value2 = new Class2WithCloseMethod(...);
> >> >         try {
> >> >            Class3WithCloseMethod value3 = new
> >> > Class3WithCloseMethod(...);
> >> >            try {
> >> >               //work gets done here
> >> >            } finally {
> >> >               value3.close();
> >> >            }
> >> >         } finally {
> >> >            value2.close();
> >> >         }
> >> >      } finally {
> >> >         value1.close();
> >> >      }
> >> >
> >> >      CASE 5
> >> >      ------
> >> >      try (Class1WithCloseMethod value1 = new
> Class1WithCloseMethod(...),
> >> >           Class2WithCloseMethod value2 = new
> Class2WithCloseMethod(...),
> >> >           Class3WithCloseMethod value3 = new
> Class3WithCloseMethod(...))
> >> > {
> >> >         //work gets done here
> >> >      } finally {
> >> >         //additional clean-up code
> >> >      }
> >> >
> >> >      Will be syntactic sugar for:
> >> >      Class1WithCloseMethod value1 = new Class1WithCloseMethod(...);
> >> >      try {
> >> >         Class2WithCloseMethod value2 = new Class2WithCloseMethod(...);
> >> >         try {
> >> >            Class3WithCloseMethod value3 = new
> >> > Class3WithCloseMethod(...);
> >> >            try {
> >> >               //work gets done here
> >> >            } finally {
> >> >               value3.close();
> >> >            }
> >> >         } finally {
> >> >            value2.close();
> >> >         }
> >> >      } finally {
> >> >         value1.close();
> >> >         //additional clean-up code
> >> >      }
> >> >
> >> >      CASE 6
> >> >      ------
> >> >      try (Class1WithCloseMethod value1 = new
> Class1WithCloseMethod(...),
> >> >           Class2WithCloseMethod value2 = new
> Class2WithCloseMethod(...),
> >> >           Class3WithCloseMethod value3 = new
> Class3WithCloseMethod(...))
> >> > {
> >> >         //work gets done here
> >> >      } catch (Exception ex) {
> >> >         //exception handling code
> >> >      } finally {
> >> >         //additional clean-up code
> >> >      }
> >> >
> >> >      Will be syntactic sugar for:
> >> >      Class1WithCloseMethod value1 = new Class1WithCloseMethod(...);
> >> >      try {
> >> >         Class2WithCloseMethod value2 = new Class2WithCloseMethod(...);
> >> >         try {
> >> >            Class3WithCloseMethod value3 = new
> >> > Class3WithCloseMethod(...);
> >> >            try {
> >> >               //work gets done here
> >> >            } finally {
> >> >               value3.close();
> >> >            }
> >> >         } finally {
> >> >            value2.close();
> >> >         }
> >> >      } catch (Exception ex) {
> >> >         //exception handling code
> >> >      } finally {
> >> >         value1.close();
> >> >         //additional clean-up code
> >> >      }
> >> >
> >> >   COMPILATION: The SPECIFICATION section above shows the desugaring
> for
> >> > each case.  Byte code would be identical to the desugared constructs.
> >> >
> >> >   TESTING: Byte code comparison of common code constructs.  If the
> byte
> >> > code is not identical to the desugared version, test fails.
> >> >
> >> >   LIBRARY SUPPORT: No.
> >> >
> >> >   REFLECTIVE APIS: No.
> >> >
> >> >   OTHER CHANGES: No.
> >> >
> >> >   MIGRATION: For each case in the SPECIFICATION section, convert the
> >> > existing code to the syntactic sugar proposal.
> >> >
> >> >
> >> > COMPATIBILITY
> >> >
> >> >   BREAKING CHANGES: None.
> >> >
> >> >   EXISTING PROGRAMS: Compile accepts both existing and new forms of
> the
> >> > "try" statement.  Byte code does not change.
> >> >
> >> > REFERENCES
> >> >
> >> >   EXISTING BUGS: None
> >> >
> >> >
> >> > --
> >> > Roger Hernandez
> >> >
> >> >
> >
> >
> >
> > --
> > Roger Hernandez
> >
>



-- 
Roger Hernandez



More information about the coin-dev mailing list