169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal/*
269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Javassist, a Java-bytecode translator toolkit.
369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * The contents of this file are subject to the Mozilla Public License Version
669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * 1.1 (the "License"); you may not use this file except in compliance with
769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * the License.  Alternatively, the contents of this file may be used under
869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * the terms of the GNU Lesser General Public License Version 2.1 or later.
969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
1069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Software distributed under the License is distributed on an "AS IS" basis,
1169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * for the specific language governing rights and limitations under the
1369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * License.
1469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */
1569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
1669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpackage javassist.util.proxy;
1769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
1869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.lang.reflect.Method;
1969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.io.BufferedOutputStream;
2069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.io.ByteArrayOutputStream;
2169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.io.DataOutputStream;
2269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.io.File;
2369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.io.FileOutputStream;
2469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.io.IOException;
2569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport java.security.ProtectionDomain;
2669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
2769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport javassist.CannotCompileException;
2869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport javassist.bytecode.ClassFile;
2969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
3069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal/**
3169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * A helper class for implementing <code>ProxyFactory</code>.
3269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * The users of <code>ProxyFactory</code> do not have to see this class.
3369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
3469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * @see ProxyFactory
3569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */
3669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpublic class FactoryHelper {
3769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static java.lang.reflect.Method defineClass1, defineClass2;
3869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
3969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    static {
4069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
4169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            Class cl = Class.forName("java.lang.ClassLoader");
4269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            defineClass1 = SecurityActions.getDeclaredMethod(
4369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        cl,
4469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        "defineClass",
4569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        new Class[] { String.class, byte[].class,
4669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                                      int.class, int.class });
4769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
4869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            defineClass2 = SecurityActions.getDeclaredMethod(
4969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        cl,
5069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        "defineClass",
5169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        new Class[] { String.class, byte[].class,
5269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                              int.class, int.class, ProtectionDomain.class });
5369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
5469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (Exception e) {
5569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException("cannot initialize");
5669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
5769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
5869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
5969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
6069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Returns an index for accessing arrays in this class.
6169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
6269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @throws RuntimeException     if a given type is not a primitive type.
6369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
6469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final int typeIndex(Class type) {
6569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Class[] list = primitiveTypes;
6669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int n = list.length;
6769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (int i = 0; i < n; i++)
6869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (list[i] == type)
6969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                return i;
7069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
7169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        throw new RuntimeException("bad type:" + type.getName());
7269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
7369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
7469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
7569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <code>Class</code> objects representing primitive types.
7669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
7769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final Class[] primitiveTypes = {
7869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE,
7969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE
8069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    };
8169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
8269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
8369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * The fully-qualified names of wrapper classes for primitive types.
8469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
8569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final String[] wrapperTypes = {
8669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        "java.lang.Boolean", "java.lang.Byte", "java.lang.Character",
8769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        "java.lang.Short", "java.lang.Integer", "java.lang.Long",
8869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        "java.lang.Float", "java.lang.Double", "java.lang.Void"
8969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    };
9069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
9169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
9269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * The descriptors of the constructors of wrapper classes.
9369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
9469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final String[] wrapperDesc = {
9569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        "(Z)V", "(B)V", "(C)V", "(S)V", "(I)V", "(J)V",
9669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        "(F)V", "(D)V"
9769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    };
9869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
9969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
10069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * The names of methods for obtaining a primitive value
10169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * from a wrapper object.  For example, <code>intValue()</code>
10269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * is such a method for obtaining an integer value from a
10369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * <code>java.lang.Integer</code> object.
10469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
10569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final String[] unwarpMethods = {
10669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        "booleanValue", "byteValue", "charValue", "shortValue",
10769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        "intValue", "longValue", "floatValue", "doubleValue"
10869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    };
10969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
11069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
11169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * The descriptors of the unwrapping methods contained
11269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * in <code>unwrapMethods</code>.
11369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
11469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final String[] unwrapDesc = {
11569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        "()Z", "()B", "()C", "()S", "()I", "()J", "()F", "()D"
11669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    };
11769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
11869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
11969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * The data size of primitive types.  <code>long</code>
12069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * and <code>double</code> are 2; the others are 1.
12169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
12269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static final int[] dataSize = {
12369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        1, 1, 1, 1, 1, 2, 1, 2
12469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    };
12569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
12669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
12769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Loads a class file by a given class loader.
12869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * This method uses a default protection domain for the class
12969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * but it may not work with a security manager or a sigend jar file.
13069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
13169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @see #toClass(ClassFile,ClassLoader,ProtectionDomain)
13269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
13369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static Class toClass(ClassFile cf, ClassLoader loader)
13469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        throws CannotCompileException
13569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    {
13669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return toClass(cf, loader, null);
13769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
13869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
13969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
14069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Loads a class file by a given class loader.
14169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
14269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @param domain        if it is null, a default domain is used.
14369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * @since 3.3
14469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
14569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static Class toClass(ClassFile cf, ClassLoader loader, ProtectionDomain domain)
14669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throws CannotCompileException
14769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    {
14869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
14969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            byte[] b = toBytecode(cf);
15069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            Method method;
15169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            Object[] args;
15269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (domain == null) {
15369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                method = defineClass1;
15469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                args = new Object[] { cf.getName(), b, new Integer(0),
15569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        new Integer(b.length) };
15669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
15769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            else {
15869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                method = defineClass2;
15969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                args = new Object[] { cf.getName(), b, new Integer(0),
16069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        new Integer(b.length), domain };
16169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
16269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
16369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return toClass2(method, loader, args);
16469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
16569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (RuntimeException e) {
16669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw e;
16769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
16869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (java.lang.reflect.InvocationTargetException e) {
16969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new CannotCompileException(e.getTargetException());
17069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
17169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (Exception e) {
17269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new CannotCompileException(e);
17369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
17469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
17569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
17669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static synchronized Class toClass2(Method method,
17769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                                        ClassLoader loader, Object[] args)
17869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        throws Exception
17969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    {
18069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        SecurityActions.setAccessible(method, true);
18169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        Class clazz = (Class)method.invoke(loader, args);
18269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        SecurityActions.setAccessible(method, false);
18369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return clazz;
18469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
18569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
18669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static byte[] toBytecode(ClassFile cf) throws IOException {
18769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        ByteArrayOutputStream barray = new ByteArrayOutputStream();
18869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        DataOutputStream out = new DataOutputStream(barray);
18969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
19069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            cf.write(out);
19169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
19269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        finally {
19369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            out.close();
19469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
19569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
19669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return barray.toByteArray();
19769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
19869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
19969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
20069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Writes a class file.
20169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
20269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public static void writeFile(ClassFile cf, String directoryName)
20369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throws CannotCompileException {
20469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
20569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            writeFile0(cf, directoryName);
20669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
20769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (IOException e) {
20869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new CannotCompileException(e);
20969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
21069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
21169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
21269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static void writeFile0(ClassFile cf, String directoryName)
21369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throws CannotCompileException, IOException {
21469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        String classname = cf.getName();
21569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        String filename = directoryName + File.separatorChar
21669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                + classname.replace('.', File.separatorChar) + ".class";
21769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int pos = filename.lastIndexOf(File.separatorChar);
21869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (pos > 0) {
21969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            String dir = filename.substring(0, pos);
22069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (!dir.equals("."))
22169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                new File(dir).mkdirs();
22269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
22369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
22469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        DataOutputStream out = new DataOutputStream(new BufferedOutputStream(
22569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                new FileOutputStream(filename)));
22669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
22769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            cf.write(out);
22869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
22969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (IOException e) {
23069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw e;
23169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
23269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        finally {
23369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            out.close();
23469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
23569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
23669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal}
237