using try-with-resources here, which ensures proper stream closure