You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ka...@apache.org on 2014/06/24 09:37:47 UTC
svn commit: r1605021 - in /db/derby/code/trunk/java:
testing/org/apache/derbyTesting/functionTests/tests/lang/
testing/org/apache/derbyTesting/junit/
tools/org/apache/derby/impl/tools/planexporter/ tools/org/apache/derby/tools/
Author: kahatlen
Date: Tue Jun 24 07:37:46 2014
New Revision: 1605021
URL: http://svn.apache.org/r1605021
Log:
DERBY-6629: Restrict privileged operation in CreateXMLFile
Remove the privileged operation in the CreateXMLFile class. Instead, let
the caller do the privileged operation and pass in a Writer object that
CreateXMLFile can write the file to.
Make the tests invoke the plan exporter tool via its main class instead
of calling the internal API.
Modified:
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/XplainStatisticsTest.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java
db/derby/code/trunk/java/tools/org/apache/derby/impl/tools/planexporter/CreateXMLFile.java
db/derby/code/trunk/java/tools/org/apache/derby/tools/PlanExporter.java
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/XplainStatisticsTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/XplainStatisticsTest.java?rev=1605021&r1=1605020&r2=1605021&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/XplainStatisticsTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/XplainStatisticsTest.java Tue Jun 24 07:37:46 2014
@@ -21,9 +21,11 @@
package org.apache.derbyTesting.functionTests.tests.lang;
+import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintStream;
import java.net.MalformedURLException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
@@ -47,7 +49,7 @@ import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.derby.impl.tools.planexporter.AccessDatabase;
-import org.apache.derby.impl.tools.planexporter.CreateXMLFile;
+import org.apache.derby.tools.PlanExporter;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
import org.apache.derbyTesting.junit.JDBC;
@@ -536,9 +538,11 @@ public class XplainStatisticsTest extend
/**
*
* @param s
+ * @param exportPlan whether or not the PlanExporter tool should be used
+ * to export the plan of the recorded statements
* @throws Exception
*/
- private static void disableXplainStyle(Statement s)
+ private static void disableXplainStyle(Statement s, boolean exportPlan)
throws Exception
{
s.execute("call SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(0)");
@@ -547,31 +551,22 @@ public class XplainStatisticsTest extend
* Added by DERBY-4587 to test the generation of XML files
* from PlanExporter tool.
*/
- String stmt_id="";
- ResultSet rs;
- AccessDatabase access;
+ if (exportPlan) {
+ String dbUrl = s.getConnection().getMetaData().getURL();
+
+ ResultSet rs = s.executeQuery(
+ "select stmt_id from XPLTEST.sysxplain_statements");
+ while (rs.next()) {
+ String stmt_id = rs.getString(1);
+ String output = invokePlanExporterTool(
+ dbUrl, "XPLTEST", stmt_id, "-xml",
+ SupportFilesSetup.getReadWriteFileName(stmt_id + ".xml"));
+
+ // Expect the plan exporter tool to print nothing on success.
+ assertEquals("Unexpected output from PlanExporter", "", output);
+ }
+ }
- rs = s.executeQuery(
- "select stmt_id from XPLTEST.sysxplain_statements");
- while (rs.next())
- {
- stmt_id = rs.getString(1);
- access =
- new AccessDatabase(s.getConnection(), "XPLTEST", stmt_id);
- if(access.initializeDataArray()){
- access.createXMLFragment();
- access.markTheDepth();
-
- CreateXMLFile xml_file = new CreateXMLFile(access);
- xml_file.writeTheXMLFile(
- access.statement(),
- access.time(),
- access.getData(),
- SupportFilesSetup.getReadWriteURL(stmt_id + ".xml")
- .getPath(),
- null);
- }
- }
s.execute("call syscs_util.syscs_set_xplain_schema('')");
}
@@ -601,6 +596,37 @@ public class XplainStatisticsTest extend
rs.close();
}
+ /**
+ * Invoke the PlanExporter tool.
+ *
+ * @param args the command line arguments to pass to the tool
+ * @return the output printed by the tool (typically an empty string
+ * on successful execution)
+ */
+ private static String invokePlanExporterTool(String... args) {
+ PrintStream out = System.out;
+ PrintStream err = System.err;
+
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ PrintStream testOutput = new PrintStream(byteOut);
+
+ // Redirect System.out and System.err so that the output
+ // can be captured.
+ setSystemOut(testOutput);
+ setSystemErr(testOutput);
+
+ try {
+ PlanExporter.main(args);
+ } finally {
+ // Restore System.out and System.err.
+ setSystemOut(out);
+ setSystemErr(err);
+ }
+
+ testOutput.flush();
+ return byteOut.toString();
+ }
+
// Can be used internally when diagnosing failed tests.
//
private void dumpResultSets(Statement s)
@@ -806,24 +832,29 @@ public class XplainStatisticsTest extend
public void testPlanExporterIllegalFileAccess()
throws Exception
{
- AccessDatabase access =
- new AccessDatabase(getConnection(), "NoSuchSchema", "nostmt");
- CreateXMLFile xml_file = new CreateXMLFile(access);
- try
- {
- xml_file.writeTheXMLFile("nostmt", "notime", null,
- "/illegal.xml", null);
- fail("Expected exception for illegal file access");
- }
- catch (java.security.AccessControlException ace)
- {
- // Expected this exception to be thrown
- }
- catch (Exception e)
- {
- e.printStackTrace();
- fail(e.getMessage());
- }
+ // Make sure there is a statement with recorded statistics.
+ Statement s = createStatement();
+ enableXplainStyle(s);
+ JDBC.assertDrainResults(s.executeQuery("values 1"));
+ disableXplainStyle(s, true);
+
+ // Get the id of the statement.
+ ResultSet rs = s.executeQuery(
+ "select stmt_id from XPLTEST.sysxplain_statements");
+ assertTrue("no statements", rs.next());
+ String stmt_id = rs.getString("stmt_id");
+ JDBC.assertEmpty(rs);
+
+ // Try to write the plan to a file that the tool does not have
+ // permission to write to.
+ String output = invokePlanExporterTool(
+ getConnection().getMetaData().getURL(), "XPLTEST",
+ stmt_id, "-xml", "/illegal.xml");
+
+ // The plan exporter tool should fail with a permission error.
+ if (!output.contains("java.security.AccessControlException")) {
+ fail("Unexpected output from PlanExporter: " + output);
+ }
}
/**
@@ -868,21 +899,18 @@ public class XplainStatisticsTest extend
rs.close();
// Create the XML file. This used to result in a syntax error.
- AccessDatabase access =
- new AccessDatabase(getConnection(), schema, stmtId);
- assertTrue(access.initializeDataArray());
- access.createXMLFragment();
- access.markTheDepth();
- CreateXMLFile create = new CreateXMLFile(access);
- create.writeTheXMLFile(
- access.statement(),
- access.time(),
- access.getData(),
- SupportFilesSetup.getReadWriteURL(stmtId + ".xml").getPath(),
- null);
+ String output = invokePlanExporterTool(
+ getConnection().getMetaData().getURL(),
+ schema,
+ stmtId,
+ "-xml",
+ SupportFilesSetup.getReadWriteFileName(stmtId + ".xml"));
+
+ // Expect empty output on successful execution of the tool.
+ assertEquals("Unexpected output from PlanExporter", "", output);
// If we have the required libraries for parsing XML files, verify
- // that the output contains valid data.
+ // that the XML file contains valid data.
if (XML.classpathMeetsXMLReqs()) {
assertEquals(query, readStatement(stmtId));
}
@@ -964,7 +992,7 @@ public class XplainStatisticsTest extend
new String[][] { {"Belize"}, {"Costa Rica"}, {"El Salvador"},
{"Guatemala"}, {"Honduras"}, {"Nicaragua"} } );
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// The statement should have been executed as a PROJECTION
// wrapped around a TABLESCAN. The TABLESCAN should have had
@@ -1112,7 +1140,7 @@ public class XplainStatisticsTest extend
"SELECT country from countries "+
"WHERE region = 'Central America'" ));
clearXplainOnlyMode(s);
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// dumpStatements(s);
// dumpResultSets(s);
@@ -1146,7 +1174,7 @@ public class XplainStatisticsTest extend
"select sql_text from syscs_diag.transaction_table " +
"where status != 'IDLE'" ));
clearXplainOnlyMode(s);
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
//dumpStatements(s);
//dumpResultSets(s);
@@ -1181,7 +1209,7 @@ public class XplainStatisticsTest extend
JDBC.assertEmpty(s.executeQuery(selectStatement));
clearXplainOnlyMode(s);
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
//dumpStatements(s);
//dumpResultSets(s);
@@ -1239,7 +1267,7 @@ public class XplainStatisticsTest extend
enableXplainOnlyMode(s);
JDBC.assertEmpty(ps.executeQuery());
clearXplainOnlyMode(s);
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
//dumpStatements(s);
//dumpResultSets(s);
@@ -1303,7 +1331,7 @@ public class XplainStatisticsTest extend
enableXplainOnlyMode(s);
JDBC.assertEmpty(ps.executeQuery());
clearXplainOnlyMode(s);
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// Verify that statistics were collected.
JDBC.assertDrainResults(
@@ -1333,7 +1361,7 @@ public class XplainStatisticsTest extend
{"Europe", "29"}, {"Middle East", "7"},
{"North Africa", "5"}, {"North America", "3"},
{"Pacific Islands", "3"}, {"South America", "11"} } );
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
//dumpStatements(s);
//dumpResultSets(s);
@@ -1574,7 +1602,7 @@ public class XplainStatisticsTest extend
JDBC.assertUnorderedResultSet(s.executeQuery(selectStatement),
new String[][] { {"AA1112"}, {"AA1114"}, {"AA1116"} } );
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// This query should have been executed as a PROJECTION whose child
// is a ROWIDSCAN whose child is an INDEXSCAN. The INDEXSCAN should
@@ -1669,7 +1697,7 @@ public class XplainStatisticsTest extend
"SELECT region from countries where country = 'Cameroon'";
JDBC.assertSingleValueResultSet(s.executeQuery(selectStatement),
"Africa");
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
JDBC.assertUnorderedResultSet(s.executeQuery(
"select op_identifier from xpltest.sysxplain_resultsets"),
new String[][] {
@@ -1744,7 +1772,7 @@ public class XplainStatisticsTest extend
{"North Africa", "5"}, {"North America", "3"},
{"Pacific Islands", "3"}, {"South America", "11"} } );
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// This statement is executed as a PROJECTION with a child GROUPBY
// with a child PROJECTION with a child TABLESCAN. The TABLESCAN
@@ -1843,7 +1871,7 @@ public class XplainStatisticsTest extend
// Execute the statement and throw away the results. We just want
// to look at the statistics.
s.executeQuery(selectStatement).close();
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
JDBC.assertSingleValueResultSet(s.executeQuery(
"select count(*) from xpltest.sysxplain_sort_props"), "1");
@@ -1907,7 +1935,7 @@ public class XplainStatisticsTest extend
String selectStatement =
"select count(distinct region) from countries";
JDBC.assertSingleValueResultSet(s.executeQuery(selectStatement), "12");
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// The above statement results in the query execution:
// PROJECTION(AGGREGATION(PROJECTION(TABLESCAN)))
@@ -1969,7 +1997,7 @@ public class XplainStatisticsTest extend
"insert into AIRLINES values " +
"('AA','Amazonian Airways',0.18,0.03,0.5,1.5,20,10,5)";
int numRows = s.executeUpdate(insertStatement);
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
assertEquals("Failed to insert into AIRLINES", 1, numRows);
JDBC.assertUnorderedResultSet(s.executeQuery(
"select stmt_type, stmt_text " +
@@ -2062,7 +2090,7 @@ public class XplainStatisticsTest extend
enableXplainStyle(s);
numRows = s.executeUpdate(updateStatement);
assertEquals("Failed to update AIRLINES", 1, numRows);
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
JDBC.assertUnorderedResultSet(s.executeQuery(
"select stmt_type, stmt_text " +
" from xpltest.sysxplain_statements"),
@@ -2219,7 +2247,7 @@ public class XplainStatisticsTest extend
enableXplainStyle(s);
numRows = s.executeUpdate(deleteStatement);
assertEquals("Failed to delete from AIRLINES", 1, numRows);
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
JDBC.assertUnorderedResultSet(s.executeQuery(
"select stmt_type, stmt_text " +
" from xpltest.sysxplain_statements"),
@@ -2291,7 +2319,7 @@ public class XplainStatisticsTest extend
String selectStatement =
"select region from countries order by country";
s.executeQuery(selectStatement).close(); // Discard the results
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// The above statement results in the query execution:
// PROJECTION(SORT(PROJECTION(TABLESCAN)))
@@ -2373,7 +2401,7 @@ public class XplainStatisticsTest extend
" union " +
"select country from countries where region = 'Africa'";
s.executeQuery(selectStatement).close(); // Discard the results
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// The above statement results in the query execution:
// SORT(UNION(PROJECTION(TABLESCAN),PROJECTION(TABLESCAN)))
@@ -2477,7 +2505,11 @@ public class XplainStatisticsTest extend
String ddlStatement =
"create table t1 (a int, b char(10), c timestamp)";
s.executeUpdate(ddlStatement);
- disableXplainStyle(s);
+
+ // Don't run the PlanExporter tool on this statement. There is no
+ // result set graph recorded for DDL statements, so the PlanExporter
+ // tool will complain.
+ disableXplainStyle(s, false);
JDBC.assertUnorderedResultSet(s.executeQuery(
"select stmt_type, stmt_text " +
@@ -2498,7 +2530,7 @@ public class XplainStatisticsTest extend
String selectStatement =
"select max(country_iso_code) from countries";
s.executeQuery(selectStatement).close();
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// The above query is executed as
// PROJECTION(AGGREGATION(PROJECTION(LASTINDEXKEYSCAN)))
@@ -2613,7 +2645,7 @@ public class XplainStatisticsTest extend
{"ABQ",null},{"OKC",null},{"AKL",null},{"HNL",null},
{"AKL",null},{"NRT",null}
});
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// We should get a Nested Loop Outer Join which reads 10 rows
// from the left (SEEN_ROWS), constructs 10 EMPTY_RIGHT_ROWS,
@@ -2708,7 +2740,7 @@ public class XplainStatisticsTest extend
enableXplainStyle(s);
for (int i = 0; i < searches.length; i++)
s.executeQuery(searches[i]).close();
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
ResultSet rs = s.executeQuery(
"select s.stmt_text, sp.start_position, sp.stop_position " +
@@ -2781,7 +2813,7 @@ public class XplainStatisticsTest extend
String selectStatement = "select x from t";
JDBC.assertUnorderedResultSet(s.executeQuery(selectStatement),
new String[][] { {"1"},{"2"},{"4"} });
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// There should be a CONSTRAINTSCAN result set with a SCAN PROPS
// which indicates that we visited 1 deleted row while scanning
@@ -2854,7 +2886,7 @@ public class XplainStatisticsTest extend
Statement s = createStatement();
enableXplainStyle(s);
JDBC.assertEmpty(s.executeQuery(sql));
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
// Now, see if we find the query among the recorded statements.
PreparedStatement ps = prepareStatement(
@@ -2905,7 +2937,7 @@ public class XplainStatisticsTest extend
enableXplainStyle(s);
JDBC.assertEmpty(s.executeQuery(queryText));
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
ResultSet rs = s.executeQuery(
"SELECT STMT_ID, STMT_TEXT FROM XPLTEST.SYSXPLAIN_STATEMENTS");
@@ -2941,7 +2973,7 @@ public class XplainStatisticsTest extend
enableXplainStyle(s);
JDBC.assertEmpty(s.executeQuery(queryText));
- disableXplainStyle(s);
+ disableXplainStyle(s, true);
ResultSet rs = s.executeQuery(
"SELECT STMT_ID, STMT_TEXT FROM XPLTEST.SYSXPLAIN_STATEMENTS");
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java?rev=1605021&r1=1605020&r2=1605021&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java Tue Jun 24 07:37:46 2014
@@ -257,7 +257,7 @@ public abstract class BaseTestCase
*
* @param out the new stream
*/
- protected void setSystemOut(final PrintStream out) {
+ protected static void setSystemOut(final PrintStream out) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
System.setOut(out);
@@ -271,7 +271,7 @@ public abstract class BaseTestCase
*
* @param err the new stream
*/
- protected void setSystemErr(final PrintStream err) {
+ protected static void setSystemErr(final PrintStream err) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
System.setErr(err);
Modified: db/derby/code/trunk/java/tools/org/apache/derby/impl/tools/planexporter/CreateXMLFile.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/tools/org/apache/derby/impl/tools/planexporter/CreateXMLFile.java?rev=1605021&r1=1605020&r2=1605021&view=diff
==============================================================================
--- db/derby/code/trunk/java/tools/org/apache/derby/impl/tools/planexporter/CreateXMLFile.java (original)
+++ db/derby/code/trunk/java/tools/org/apache/derby/impl/tools/planexporter/CreateXMLFile.java Tue Jun 24 07:37:46 2014
@@ -21,14 +21,8 @@
package org.apache.derby.impl.tools.planexporter;
-import java.io.BufferedWriter;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.OutputStreamWriter;
import java.io.Writer;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
/**
* This class is to create the final xml file, that will be used
@@ -46,15 +40,12 @@ public class CreateXMLFile {
/**
* @param stmt statement executed
* @param time time which the statement was executed
- * @param data large xml data string array
- * @param file_name name of the file to be written
+ * @param out where to write the XML file
* @param xsl_sheet_name name of the style sheet
- * @throws PrivilegedActionException
* @throws IOException
- * @throws PrivilegedActionException
*/
public void writeTheXMLFile(String stmt, String time,
- TreeNode[] data, final String file_name, String xsl_sheet_name)
+ Writer out, String xsl_sheet_name)
throws IOException {
String defaultXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
@@ -68,20 +59,6 @@ public class CreateXMLFile {
String childTagStart = "<details>\n";
String childTagEnd = "</details>\n";
- FileOutputStream fos;
- try {
- fos = AccessController.doPrivileged(
- new PrivilegedExceptionAction<FileOutputStream>() {
- public FileOutputStream run() throws IOException {
- return new FileOutputStream(file_name);
- }
- });
- } catch (PrivilegedActionException pae) {
- throw (IOException) pae.getCause();
- }
-
- Writer out = new BufferedWriter(new OutputStreamWriter(fos, "UTF-8"));
-
out.write(defaultXML);
out.write(embedXSL);
@@ -106,6 +83,5 @@ public class CreateXMLFile {
out.write(childTagEnd);
out.write(parentTagEnd);
- out.close();
}
}
Modified: db/derby/code/trunk/java/tools/org/apache/derby/tools/PlanExporter.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/tools/org/apache/derby/tools/PlanExporter.java?rev=1605021&r1=1605020&r2=1605021&view=diff
==============================================================================
--- db/derby/code/trunk/java/tools/org/apache/derby/tools/PlanExporter.java (original)
+++ db/derby/code/trunk/java/tools/org/apache/derby/tools/PlanExporter.java Tue Jun 24 07:37:46 2014
@@ -22,7 +22,13 @@
package org.apache.derby.tools;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
import org.apache.derby.impl.tools.planexporter.AccessDatabase;
import org.apache.derby.impl.tools.planexporter.CreateHTMLFile;
import org.apache.derby.impl.tools.planexporter.CreateXMLFile;
@@ -225,22 +231,34 @@ public class PlanExporter {
* @param stmt statement executed
* @param time time which the statement was executed
* @param xsl name of the style sheet
- * @throws Exception
+ * @throws IOException if an error occurs when writing the XML file
*/
private static void generateXML(AccessDatabase access,
- String arg, String stmt, String time, String xsl) throws Exception{
+ String arg, String stmt, String time, String xsl)
+ throws IOException {
CreateXMLFile xmlFile = new CreateXMLFile(access);
- if(arg.toUpperCase().endsWith(".XML")){
- xmlFile.writeTheXMLFile(stmt, time,
- access.getData(),
- arg, xsl);
+ final String fileName = arg.toUpperCase().endsWith(".XML")
+ ? arg : (arg + ".xml");
+
+ Writer out;
+ try {
+ out = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Writer>() {
+ @Override
+ public Writer run() throws IOException {
+ return new OutputStreamWriter(
+ new FileOutputStream(fileName), "UTF-8");
+ }
+ });
+ } catch (PrivilegedActionException pae) {
+ throw (IOException) pae.getCause();
}
- else{
- xmlFile.writeTheXMLFile(stmt, time,
- access.getData(),
- arg.concat(".xml"),
- xsl);
+
+ try {
+ xmlFile.writeTheXMLFile(stmt, time, out, xsl);
+ } finally {
+ out.close();
}
}